arp \
dhcpv4 \
dhcpv6 \
- eap/aka \
- eap/sim \
+ eap/aka-sim \
ethernet \
freeradius \
radius \
type = peap
# type = fast
# type = aka
- type = sim
+# type = sim
#
# ### EAP-MD5
# * `Comp128v1`
# * `Comp128v2`
# * `Comp128v3` (default if no &control:SIM-OPc)
- # * `Milenage` (default if &control:SIM-OPc is available)
+ # * `Comp128v4` (default if &control:SIM-OPc is available)
#
# NOTE: In general operators will not provide the Ki (subscriber
# key) for their `SIM cards`, as the Ki is considered highly
#
sim {
#
- # send_result_ind:: Send a protected success message.
+ # request_identity:: Send a SIM-Start message to Requests
+ # an additional identity to the one from the EAP-Identity-Response.
+ #
+ # If `yes` - We send a request with AT_SUBTYPE SIM-start
+ # with an AT_ANY_ID_REQ attribute to prompt the supplicant
+ # to send a new identity.
+ #
+ # The type of identity requested can be controlled by adding
+ # one of the following attributes from the
+ # `recv Identity-Response { ... }` section of the specified
+ # EAP-SIM virtual_server, to the reply list:
+ #
+ # - &reply:Any-Id-Req
+ # - &reply:Fullauth-Id-Req
+ # - &reply:Permanent-Id-Req
+ #
+ # Even if this configuration item is set to 'no', additional
+ # identities can be requested by policy.
+ # See sites-available/eap-aka-sim for details.
+ #
+# request_identity = no
+
+ #
+ # ephemeral_id_length:: The length of any pseudonyms or
+ # fastauth identities we generate (not including hint byte).
+ #
+ # See sites-available/eap-aka-sim for details on how to trigger
+ # the generation of pseudonym or fastauth identities.
+ #
+# ephemeral_id_length = 14
+
+ #
+ # strip_permanent_identity_hint:: Strip the identity hint when
+ # copying &EAP-Identity or &Identity to &Permanent-Identity.
+ #
+ # The stripping is only performed if this config item is set
+ # to yes, and the hint byte matches what we'd expect for the
+ # EAP-Method being executed.
+ #
+# strip_permanent_identity_hint = yes
+
+ #
+ # protected_success:: Send a protected success message.
#
# If `yes` - indicate to the peer that we'd like to send
# a protected success message. If the peer agrees and
# authentication succeeds, send a `SIM-Notification` (Success)
# packet protected with an `AT_MAC` attribute.
#
- # After the Notification is `ACKed` by the peer, we send
+ # After the Notification is ACKed by the peer, we send
# the final `EAP-Success` packet containing `MPPE` keys.
#
-# send_result_ind = yes
+# protected_success = yes
+
+ #
+ # virtual_server:: EAP-SIM virtual server containing policy
+ # sections. This must be set, EAP-SIM will not function
+ # without it, as certain operations like vector gathering
+ # require user configuration.
+ #
+ virtual_server = eap-aka-sim
}
#
# ### EAP-AKA
#
- # This takes no configuration.
- #
aka {
#
- # network_name:: Network ID as described by RFC 5448.
+ # protected_success:: Send protected success messages
+ #
+# protected_success = no
+
+ #
+ # virtual_server:: Same as for `TTLS`, `PEAP`, etc.
+ #
+# virtual_server = ""
+
+ # request_identity:: Send a AKA-Identity message to request
+ # an additional identity to the one from the EAP-Identity-Response.
+ #
+ # If `yes` - We send a request with AT_SUBTYPE AKA-Identity
+ # with an AT_ANY_ID_REQ attribute to prompt the supplicant
+ # to send a new identity.
+ #
+ # The type of identity requested can be controlled by adding
+ # one of the following attributes from the
+ # `recv Identity-Response { ... }` section of the specified
+ # EAP-AKA virtual_server, to the reply list:
+ #
+ # - &reply:Any-Id-Req
+ # - &reply:Fullauth-Id-Req
+ # - &reply:Permanent-Id-Req
+ #
+ # Even if this configuration item is set to 'no', additional
+ # identities can be requested by policy.
+ # See sites-available/eap-aka-sim for details.
+ #
+# request_identity = no
+
+ #
+ # ephemeral_id_length:: The length of any pseudonyms or
+ # fastauth identities we generate (not including hint byte).
+ #
+ # See sites-available/eap-aka-sim for details on how to trigger
+ # the generation of pseudonym or fastauth identities.
+ #
+# ephemeral_id_length = 14
+
+ #
+ # strip_permanent_identity_hint:: Strip the identity hint when
+ # copying &EAP-Identity or &Identity to &Permanent-Identity.
+ #
+ # The stripping is only performed if this config item is set
+ # to yes, and the hint byte matches what we'd expect for the
+ # EAP-Method being executed.
+ #
+# strip_permanent_identity_hint = yes
+
+ #
+ # protected_success:: Send a protected success message.
+ #
+ # If `yes` - indicate to the peer that we'd like to send
+ # a protected success message. If the peer agrees and
+ # authentication succeeds, send a `AKA-Notification` (Success)
+ # packet protected with an `AT_MAC` attribute.
+ #
+ # After the Notification is ACKed by the peer, we send
+ # the final `EAP-Success` packet containing `MPPE` keys.
+ #
+# protected_success = yes
+
+ #
+ # prefer_aka_prime:: Send the AT_BIDDING attribute in
+ # AKA-Challenge messages.
+ #
+ # When AT_BIDDING is sent in a AKA-Challenge and the supplicant
+ # supports EAP-AKA-Prime, RFC 5448 states the supplicant
+ # should abort the authentication attempt as a bidding down
+ # attack may have occurred.
+ #
+ # If a value is not provided for this configuration item
+ # it will be determined automatically by whether the
+ # EAP-AKA-Prime EAP method is enabled.
+ #
+# prefer_aka_prime = yes
+
+ #
+ # virtual_server:: EAP-AKA virtual server containing policy
+ # sections. This must be set, EAP-AKA will not function
+ # without it, as certain operations like vector gathering
+ # require user configuration.
+ #
+ virtual_server = eap-aka-sim
+ }
+
+ #
+ # ### EAP-AKA-Prime
+ #
+ aka-prime {
+ #
+ # network_name:: The default value used for AT_KDF_INPUT
+ #
+ # EAP-AKA-Prime mixes various keys with a network identifier
+ # specifying the user's point of connection into the network
+ # to prevent vectors being used where they were not intended
+ # to be.
+ #
+ # The value of the AT_KDF_INPUT can be overriden in the
+ # eap-aka-prime virtual server with &reply:KDF-Input if
+ # this value needs to be determined dynamically.
+ #
+ # How this value should be created and formatted is specified
+ # in 3GPP.24.302.
#
# network_name = ""
#
- # request_identity:: Always send an identity request before a challenge.
+ # request_identity:: Send a AKA-Identity message to request
+ # an additional identity to the one from the EAP-Identity-Response.
#
-# request_identity = ""
+ # If `yes` - We send a request with AT_SUBTYPE AKA-Identity
+ # with an AT_ANY_ID_REQ attribute to prompt the supplicant
+ # to send a new identity.
+ #
+ # The type of identity requested can be controlled by adding
+ # one of the following attributes from the
+ # `recv Identity-Response { ... }` section of the specified
+ # EAP-AKA virtual_server, to the reply list:
+ #
+ # - &reply:Any-Id-Req
+ # - &reply:Fullauth-Id-Req
+ # - &reply:Permanent-Id-Req
+ #
+ # Even if this configuration item is set to 'no', additional
+ # identities can be requested by policy.
+ # See sites-available/eap-aka-sim for details.
+ #
+# request_identity = no
#
- # protected_success:: Send protected success messages
+ # ephemeral_id_length:: The length of any pseudonyms or
+ # fastauth identities we generate (not including hint byte).
#
-# protected_success = no
+ # See sites-available/eap-aka-sim for details on how to trigger
+ # the generation of pseudonym or fastauth identities.
+ #
+# ephemeral_id_length = 14
#
- # virtual_server:: Same as for `TTLS`, `PEAP`, etc.
+ # strip_permanent_identity_hint:: Strip the identity hint when
+ # copying &EAP-Identity or &Identity to &Permanent-Identity.
#
-# virtual_server = ""
+ # The stripping is only performed if this config item is set
+ # to yes, and the hint byte matches what we'd expect for the
+ # EAP-Method being executed.
+ #
+# strip_permanent_identity_hint = yes
+
+ #
+ # protected_success:: Send a protected success message.
+ #
+ # If `yes` - indicate to the peer that we'd like to send
+ # a protected success message. If the peer agrees and
+ # authentication succeeds, send a `AKA-Notification` (Success)
+ # packet protected with an `AT_MAC` attribute.
+ #
+ # After the Notification is ACKed by the peer, we send
+ # the final `EAP-Success` packet containing `MPPE` keys.
+ #
+# protected_success = yes
+
+ #
+ # virtual_server:: EAP-AKA virtual server containing policy
+ # sections. This must be set, EAP-AKA-Prime will not function
+ # without it, as certain operations like vector gathering
+ # require user configuration.
+ #
+ virtual_server = eap-aka-sim
}
}
--- /dev/null
+# -*- text -*-
+#
+# :toc:
+#
+# $Id$
+
+#######################################################################
+#
+# = EAP-SIM/EAP-AKA/EAP-AKA-Prime policy server
+#
+# This server is an example of applying custom, site-specific policies
+# to eap-aka['] based authentication.
+# As much control is given to the user to make decisions about which
+# AT_* attributes will be returned to the supplicant. Wherever
+# possible the internal state of the EAP-AKA module will be updated
+# to reflect the attributes returned from sections.
+#
+
+######################################################################
+#
+# ## Packet Processing sections
+#
+# Sections in this server may behave slightly differently to sections
+# in other virtual servers, as they're being called directly by the
+# EAP-AKA module.
+#
+# In general, the following rcodes signify a failure to the EAP-AKA
+# state machine:
+#
+# - `reject`
+# - `fail`
+# - `invalid`
+# - `userlock`
+#
+# Returning failure rcodes in the majority of sections will caused the
+# authentication to fail.
+#
+# In some instances however, we ignore the section rcode, as the standard
+# does not allow for an orderly rejection of the supplicant.
+#
+# Returning failure rcodes will be ignored in the following sections, and
+# will instead generate a warning:
+#
+# - `send EAP-Failure` already rejecting the auth attempt.
+# - `send Failure-Notification` already rejecting the auth attempt.
+# - `recv Failure-Notification` already rejecting the auth attempt.
+# - `recv Success-Notification` sending failure after EAP-Response/
+# Success Notification-ACK would
+# violate the standard.
+# - `recv Authentication-Reject` already rejecting the auth attempt.
+# - `recv Client-Error` already rejecting the auth attempt.
+# - `clear Pseudonym` nothing more we can do...
+# - `clear Session` nothing more we can do...
+#
+# For sections dealing with identity processing the `notfound` rcode
+# will trigger identity negotiation. The EAP-AKA module will then request
+# additional identity values from the supplicant. See the `recv
+# EAP-Identity-Response` and `recv AKA-Identity-Response` sections for more
+# details.
+#
+# NOTE: Setting the EAP-AKA subtype attribute is not supported, and any
+# attempts to modify it will be ignored (you are not trusted to know how to
+# run the EAP-AKA state machine better than the EAP-AKA module).
+#
+######################################################################
+
+server eap-aka-sim {
+ #
+ # This is a unified configuration example for EAP-SIM, EAP-AKA
+ # and EAP-AKA-Prime. Many of the sections are identical between
+ # the three EAP methods.
+ #
+ # The main differences are between EAP-SIM and EAP-AKA['] where
+ # additional identity processing sections are used.
+ #
+# namespace = eap-sim
+ namespace = eap-aka
+# namespace = eap-aka-prime
+
+ #
+ # ### `recv Identity-Response { ... }` - Process an EAP-Identity-Response or AKA-Identity value
+ #
+ # This is usually the first section called when starting a new EAP-AKA
+ # session, and will be called for each round of identity negotiation.
+ #
+ # The following request attributes are available:
+ # - `Identity` the identity value received in the
+ # EAP-Identity-Response.
+ # - `Permanent-Identity` If the hint byte at the start of the
+ # identity string indicated it was
+ # permanent.
+ #
+ # - `Identity-Type` The type of identity the 'hint' byte
+ # at the start of the identity indicates.
+ # - `Permanent`
+ # - `Pseudonym`
+ # - `Fastauath`
+ #
+ # - `Method-Hint` The EAP-Method the 'hint' byte
+ # at the start of the identity indicates:
+ # - `AKA-Prime`
+ # - `AKA`
+ # - `SIM`
+ #
+ # At the end of this section the value of `&Identity-Type` will be
+ # checked to see if any additional identity processing steps are
+ # required.
+ #
+ # The following actions are associated with the various
+ # `Identity-Type` values:
+ # - `Permanent`:: The current `&Identity` value will be copied to
+ # `&session-state:Permanent-Identity`. The hint byte
+ # will be stripped if the hint matches the current
+ # EAP-Method. If the hint byte does not match
+ # the current EAP-Method the complete value of
+ # Identity will be copied. In both cases the realm
+ # will also be stripped. You can override the
+ # identity stored here in policy.
+ # - `Pseudonym`:: `load pseudonym { ... }` will be called.
+ # - `Fastauth`:: `load session { ... }` will be called.
+ #
+ # #### Identity hints
+ #
+ # In EAP-AKA['] and EAP-SIM, the first byte of the identity is used as
+ # a hint about what type of identity has been provided by the
+ # supplicant and which EAP method should be started.
+ #
+ # `Identity-Type` and `Method-Hint` are added by the EAP-AKA module as
+ # a convenience to help determine if additional identities should be
+ # requested.
+ #
+ # If for whatever reason the provided ID isn't not recognised or
+ # cannot be processed, you should return `notfound` from this section
+ # to request an additional identity from the supplicant.
+ #
+ # Adding any of the following attributes to the reply list will also
+ # cause an additional identity to bre request from the supplicant.
+ #
+ # - `Permanent-Id-Req` request the supplicants `Permanent` Id
+ # usually its IMSI number.
+ # - `Fullauth-Id-Req` request either a `Permanent` or a
+ # `Pseudonym` identity`.
+ # - `Any-Id-Req` request any identity
+ # (`Fastauth`, `Pseudonym`, `Permanent``).
+ #
+ # NOTE: We do not currently support handover between the EAP-AKA/SIM
+ # modules. Usually this is not an issue because EAP Negotiation
+ # allows the supplicant and server to negotiate a mutually acceptable
+ # EAP type. If, however, you want to avoid the additional roundtrips
+ # involved in that negotiation, you can use
+ # `%{aka_sim_id_method:User-Name}` in the top level virtual server,
+ # which will return the EAP method hinted by the identity, and then
+ # set `&control:EAP-Type` to an appropriate value before calling the
+ # EAP module.
+ #
+ # #### Pseudonyms
+ #
+ # When receiving a `Identity-Type = Pseudonym` request, the pseudonym
+ # should either be decrypted or resolved to a permanent identity.
+ # Identity resolution can be done here, or in the `load pseudonym { ... }`
+ # section.
+ #
+ # If for whatever reason the identity cannot be validated, you should
+ # return `notfound` to request an additional identity.
+ #
+ # NOTE: Setting `&Method-Hint = Pseudonym` here will cause the server
+ # to execute the `load pseudonym { ... }` section next. This is
+ # sometimes useful when dealing with non-standard pseudonym identities.
+ #
+ # #### Reauthentication
+ #
+ # When receiving a `Identity-Type = Fastauth` request, the fastauth
+ # identity should be resolved in a datastore of valid fastauth
+ # identities.
+ # Session resolution can be done here, or in the `load session { ... }`
+ # section.
+ #
+ # If the identity cannot be resolved, you should return `notfound` to
+ # request an additional identity.
+ #
+ # NOTE: Setting `&Method-Hint = Fastauth` here will cause the server
+ # to execute the `load session { ... }` section next. This is
+ # sometimes useful when dealing with non-standard fastauth identities.
+ #
+ recv Identity-Response {
+ ok
+ }
+
+ #
+ # ### `send Identity-Request { ... }` - Allow user massaging of Identity-Request
+ #
+ # This section is called when a new Start or AKA-Identity request is
+ # sent to the supplicant, unless `send Start { ... }` or `send
+ # AKA-Identity { ... }` sections are provided, in which case they will
+ # be used instead.
+ #
+ send Identity-Request {
+ ok
+ }
+
+ #
+ # ### `send Start-Request { ... }` - Allow specific policies to be applied to SIM start requests
+ #
+ # This section is optional, and may be used to specify EAP-SIM only
+ # logic for sending Start requests.
+ #
+ # If this section is not provided, `send Identity-Request { ... }`
+ # will be executed instead.
+ #
+ # The Identity and Start messages perform a very similar role in
+ # EAP-SIM/EAP-AKA, the main difference being, Start messages also
+ # allow the version of EAP-SIM to be negotiated.
+ #
+# send Start-Request {
+# ok
+# }
+
+ #
+ # ### `send Start-Request { ... }` - Allow specific policies to be applied to SIM start responses
+ #
+ # This section is optional, and may be used to specify EAP-SIM only
+ # logic for sending Start requests.
+ #
+ # If this section is not provided, `send Identity-Request { ... }`
+ # will be executed instead.
+ #
+ # The Identity and Start messages perform a very similar role in
+ # EAP-SIM/EAP-AKA, the main difference being, Start messages also
+ # allow the version of EAP-SIM to be negotiated.
+ #
+# recv Start-Response {
+# ok
+# }
+
+ #
+ # ### `send AKA-Identity-Request { ... }` - Allow specific policies to be applied to AKA identity requests
+ #
+ # This section is optional, and may be used to specify
+ # EAP-AKA/EAP-AKA['] only logic for sending AKA-Identity requests.
+ #
+ # If this section is not provided, `send Identity-Request { ... }`
+ # will be executed instead.
+ #
+ # The Identity and Start messages perform a very similar role in
+ # EAP-SIM/EAP-AKA, the main difference being, Start messages also
+ # allow the version of EAP-SIM to be negotiated.
+ #
+# send AKA-Identity-Request {
+# ok
+# }
+
+ #
+ # ### `send Challenge-Request { ... }` - Acquire GSM triplets or UMTS quintuplets for the Challenge-Request
+ #
+ # NOTE: It's recommended to perform identity resolution in `recv
+ # EAP-Identity-Response` or `recv Identity-Response` and request or
+ # generate UMTS quintuplets in this section.
+ #
+ # The following attributes are added by the EAP-AKA module to the
+ # control list:
+ #
+ # - `SIM-AMF = 0x8000` Only set when using EAP-AKA'.
+ # This is a hint to any modules generating or
+ # retrieving UMTS quintuplets that the AMF
+ # separation bit should be high.
+ #
+ # The following attributes are added by the EAP-AKA module to the reply
+ # list, to be modified by policy:
+ #
+ # - `KDF-Input` Only set when using EAP-AKA'
+ # Is initially set to the value of the
+ # `network_name` configuration item. But can
+ # be set to anything. If EAP is running over
+ # RADIUS this should usually be the SSID
+ # portion of Called-Station-ID.
+ # - `Bidding` Only set when using EAP-AKA and the
+ # configuration item `prefer_aka_prime` is 'yes',
+ # or no `prefer_aka_prime` value is provided and
+ # EAP-AKA' is enabled in the same EAP module.
+ # - `Result-Ind` Set when the configuration item
+ # `protected_success` is 'yes'.
+ #
+ # Two sets of control attributes are used depending on how the
+ # quintuplets are being acquired.
+ #
+ # If generating triplets or quintuplets locally you should provide the
+ # following control attributes:
+ #
+ # - `SIM-Ki`:: Secret shared by the AuC and SIM card.
+ # - `SIM-SQN`:: Sequence number. Must be stored/incremented
+ # for each set of quintuplets generated. Used
+ # for replay protection, should be a higher
+ # value than the counterpart SQN on the SIM.
+ # - `SIM-OP`/`SIM-OPc`::Operator Variant Algorithm Configuration Field.
+ # Input to milenage. Can provide SIM-OPc if you
+ # already have access to it, else the OPc will
+ # be derived from the OP. Must match the value
+ # configured on the SIM card.
+ #
+ # If retrieving triplets from the AuC, you should should provide
+ # three sets of the following control attributes:
+ #
+ # - `SIM-KC`:: Authentication value from the AuC.
+ # - `SIM-RAND`:: User authentication challenge.
+ # - `SIM-SRES`:: Signing response.
+ #
+ # If retrieving quintuplets from a AuC, you should provide the
+ # following control attributes:
+ #
+ # - `SIM-AUTN`:: Authentication value from the AuC.
+ # - `SIM-CK`:: Ciphering Key.
+ # - `SIM-IK`:: Integrity Key.
+ # - `SIM-RAND`:: User authentication challenge.
+ # - `SIM-XRES`:: Signing response.
+ # - `SIM-SQN`:: (optional)
+ # - `SIM-AK`:: (optional)
+ #
+ # ### Pseudonyms
+ #
+ # For identity privacy you may include a `&reply:Next-Pseudonym`
+ # attribute. `&reply:Next-Pseudonym` must be a valid UTF8 string and
+ # MUST NOT contain a realm. The supplicant will decorate the identity
+ # as appropriate to ensure correct routing.
+ #
+ # The following facilitates may be useful when generating pseudonym
+ # values:
+ #
+ # - The encrypt expansions of the `rlm_cipher` module.
+ # - The `%{3gpp_pseudonym_encrypt:}` expansion.
+ # - The `%{rand:}` expansion.
+ #
+ # NOTE: Add a `&reply:Next-Pseudonym-Id` attribute in this section to
+ # avoid having the permanent Id of the SIM exposed during subsequent
+ # authentication attempts.
+ #
+ # ### Fast-Reauth
+ #
+ # For identity privacy, to reduce load on the AuC, and to improve
+ # authentication times/user experience, fast re-authentication may be
+ # used.
+ #
+ # Fast re-authentication is very similar to session-resumption in TLS
+ # based EAP methods, where the original MK (Master Key) is stored and
+ # then used to generate new MSKs (Master Session Keys) for subsequent
+ # authentication attempts.
+ #
+ # NOTE: Add a `&reply:Next-Reauth-Id` attribute in this section, to
+ # allow session-resumption for subsequent authentication attempts.
+ #
+ # ### Cryptographic identity overrides
+ #
+ # The identity provided as the value of EAP-Response/Identity or in
+ # the last processed EAP-Response/AKA-Identity or
+ # EAP-Response/SIM-Start message is used as an input to generate the
+ # MK (Master Key) in full authentication, and XKEY' in
+ # re-authentication.
+ #
+ # The identity used as inputs for these cryptographic values must be
+ # identical on both the supplicant and the EAP server, otherwise
+ # authentication will fail with the supplicant returning a
+ # EAP-Response/(AKA|SIM)-Client-Error message with an
+ # AT_CLIENT_ERROR_CODE of "unable to process packet".
+ #
+ # Some handset manufacturers have implemented identity schemes which
+ # require an identity not provided in the EAP-Response/Identity
+ # message, or sent as an AT_IDENTITY value. One such identity privacy
+ # scheme described by 3GPP S3-170116 "Privacy Protection for EAP-AKA",
+ # where the cryptographic identity is the plaintext extracted from
+ # the encrypted identity blob.
+ #
+ # If you need to implement one of these identity privacy schemes, the
+ # cryptographic identity can be specified with `&control:KDF-Identity`.
+ #
+ send Challenge-Request {
+ ok
+ }
+
+ #
+ # ### `recv Challenge-Response { ... }` - Allow additional authorization checks and logging
+ #
+ # The following rcodes have special meanings in this section:
+ #
+ # - `reject` Reject the authentication attempt, send
+ # a Failure-Notification with an
+ # AT_NOTIFICATION value of 1026
+ # (User has been temporarily denied access).
+ # - `userlock` Reject the authentication attempt, send
+ # a Failure-Notification with an
+ # AT_NOTIFICATION value of 1026
+ # (User has been temporarily denied access).
+ # - `notfound` Reject the authentication attempt, send
+ # a Failure-Notification with an
+ # AT_NOTIFICATION value of 1031
+ # (User has not subscribed to the requested service).
+ # - `invalid` Reject the authentication attempt, send
+ # a Failure-Notification with an
+ # AT_NOTIFICATION value of 0
+ # (General failure after authentication).
+ # - `Fail` Reject the authentication attempt, send
+ # a Failure-Notification with an
+ # AT_NOTIFICATION value of 0
+ # (General failure after authentication).
+ # - Anything else Continue processing the request.
+ #
+ recv Challenge-Response {
+ ok
+ }
+
+ #
+ # ### `recv Reauthentication-Request { ... }` - Allow user massaging of a reauthentication request
+ #
+ # There's not much to do here as the majority of the work for
+ # session resumption is done in `load session { ... }`.
+ #
+ # Add a `&reply:Next-Pseudonym-Id` attribute in this section if you want
+ # to allow avoid having the permanent Id of the SIM exposed during
+ # subsequent authentication attempts.
+ #
+ # Add a `&reply:Next-Reauth-Id` attribute in this section, to allow
+ # session resumption for subsequent authentication attempts.
+ #
+ # The following rcodes have special meanings in this section:
+ #
+ # - `reject` Reject the authentication attempt, send
+ # a Failure-Notification with an
+ # AT_NOTIFICATION value of 16384
+ # (General Failure).
+ # - `userlock` Reject the authentication attempt, send
+ # a Failure-Notification with an
+ # AT_NOTIFICATION value of 16384
+ # (General Failure).
+ # - `ok` Continue with reauthentication.
+ # - `updated` Continue with reauthentication.
+ # - `noop` Continue with reauthentication.
+ # - Anything else Fall back to full authentication.
+ #
+ # NOTE: You should use fresh reauthentication ID values here, else the
+ # supplicant may be tracked as they would if their permanent Id has been
+ # used.
+ #
+ send Reauthentication-Request {
+ ok
+ }
+
+ #
+ # ### `recv Reauthentication-Response { ... }` - Allow additional authorization checks and logging
+ #
+ # Uses the same rcode mappings as `recv Challenge-Response { ... }`.
+ #
+ # Will not allow a transition back to the challenge state, as it's
+ # not clear that this is allowed by RFC4186/RFC4187.
+ #
+ recv Reauthentication-Response {
+ ok
+ }
+
+ #
+ # ### `recv Client-Error { ... }` - Log the fact that the supplicant has terminated authentication
+ #
+ # The reason for the error should be available in `&Client-Error-Code`
+ #
+ # After this section is run authentication fails immediately with no
+ # EAP-Response/AKA-Notification round.
+ #
+ recv Client-Error {
+ ok
+ }
+
+ #
+ # ### `recv Authentication-Reject { ... }` - Log the fact that the supplicant failed to authenticate the AuC
+ #
+ # NOTE: Only used for EAP-AKA and EAP-AKA'
+ #
+ # The supplicant will respond with EAP-Response/AKA-Authentication-Reject
+ # whenever it cannot validate the AUTN (authentication) value provided in the
+ # EAP-Request/AKA-Challenge packet.
+ #
+ # No additional attributes are provided by the supplicant that specify
+ # the error.
+ #
+ # After this section is run authentication fails immediately with no
+ # EAP-Response/AKA-Notification round.
+ #
+ recv Authentication-Reject {
+ ok
+ }
+
+ #
+ # ### `recv Synchronization-Failure { ... }` - Allow resynchronisation of sequence numbers
+ #
+ # NOTE: Only used for EAP-AKA and EAP-AKA'
+ #
+ # In some instances the SQN on the SIM card and the SQN in the AuC can
+ # get out of sync. When this happens the supplicant will send a
+ # EAP-Response/Synchronization-Failure message with an AUTS attribute.
+ #
+ # If the original UMTS quintuplets were calculated locally and you
+ # provided Ki and OP/OPc in the Challenge-Request round, then the
+ # EAP-AKA module will automatically calculate a new SQN for you and
+ # place it in `&control:SIM-SQN`. You should store this new SQN.
+ #
+ # As a convenience, the EAP-AKA module will also repopulate
+ # &control:SIM-Ki and &control:SIM-OPc so you do not need to retrieve
+ # them again in the `send Challenge-Request { ... }` section.
+ #
+ # If the UMTS quintuplets were provided by an AuC, you should submit
+ # the AUTS value to the AuC to allow it to calculate a new SQN, then
+ # retrieve a new UMTS quintuplet in the
+ # `send Challenge-Request { ... }` section.
+ #
+ # If the rcode returned from this section does not indicate failure
+ # and a &control:SQN value is provided, we send a new challenge to
+ # the supplicant and continue, otherwise, authentication fails.
+ #
+ # NOTE: Only one Synchronisation attempt is permited per
+ # authentication attempt.
+ #
+ recv Synchronization-Failure {
+ ok
+ }
+
+ #
+ # ### `send Failure-Notification { ... }` - Allow massaging/logging of failure notifications
+ #
+ # NOTE: The rcode returned from this section is ignored.
+ #
+ send Failure-Notification {
+ ok
+ }
+
+ #
+ # ### `send Failure-Notification { ... }` - Allow logging of failure notification acknowledgements
+ #
+ # NOTE: The rcode returned from this section is ignored.
+ #
+ recv Failure-Notification-ACK {
+ ok
+ }
+
+ #
+ # ### `send Success-Notification { ... }` - Allow massaging/logging of success notifications
+ #
+ # Uses the same rcode mappings as `recv Challenge-Response { ... }`.
+ #
+ # NOTE: Only entered if protected result indications are used.
+ #
+ send Success-Notification {
+ ok
+ }
+
+ #
+ # ### `send Success-Notification-Ack { ... }` - Allow logging of success notification acknowledgements
+ #
+ # NOTE: The rcode returned from this section is ignored.
+ #
+ recv Success-Notification-ACK {
+ ok
+ }
+
+ #
+ # ### `send EAP-Success { ... }` - Allow massaging/logging of success notifications
+ #
+ # NOTE: The rcode returned from this section is ignored if we have already sent
+ # a Success-Notification, otherwise the same rcode mappings as
+ # `recv Challenge-Response { ... }` are used.
+ #
+ send EAP-Success {
+ ok
+ }
+
+ #
+ # ### `send EAP-Failure { ... }` - Allow logging of failure notification acknowledgements
+ #
+ # NOTE: The rcode returned from this section is ignored.
+ #
+ recv EAP-Failure {
+ ok
+ }
+
+ #
+ # ### `store pseudonym { ... }`
+ #
+ # If when sending a Challenge-Request Reauthentication-Request a
+ # `&reply:Next-Pseudonym-Id` attribute is found, this section will be
+ # called.
+ #
+ # If `&reply:Next-Pseudonym-Id` is empty, it will be filled with a random
+ # string of alphanumeric characters.
+ #
+ # For consistency, &reply:Next-Pseudonym-Id will also be made available
+ # temporarily in `&Next-Pseudonym-Id` for use as a key.
+ #
+ # You should store the following attributes against the key
+ # `&Next-Paeudonym-Id`:
+ #
+ # - `&session-State:Permanent-Identity`::The permanent identity of the user.
+ #
+ # If a failure rcode is returned authentication continues but the
+ # Next-Pseudonym-Id will not be sent to the supplicant.
+ #
+ # NOTE: The cache module is ideally suited for storing pseudonym
+ # data. If you're using multiple instances of FreeRADIUS in a
+ # failover cluster, you may wish to use the redis or memcached drivers
+ # to provide a unified pseudonym store.
+ #
+ # NOTE: If you're using 3GPP style encrypted pseudonyms you do not
+ # need populate this section. You should however, provide a policy
+ # for `load session { ... }`, and use that to decrypt the pseudonym.
+ #
+ store pseudonym {
+ ok
+ }
+
+ #
+ # ### `load pseudonym { ... }`
+ #
+ # This section will be called prior to entering the AKA-CHALLENGE state.
+ #
+ # You should restore the contents of the following attributes using
+ # `&Next-Pseudonym-Id` as a key:
+ #
+ # - `&session-State:Permanent-Identity`::The permanent identity of the user.
+ #
+ # NOTE: If you're using 3GPP style encrypted pseudonyms, you should
+ # decrypt the pseudonym here, and store the decrypted value in
+ # `&session-state:Permanent-Identity`.
+ #
+ load pseudonym {
+ ok
+ }
+
+ #
+ # ### `clear pseudonym { ... }`
+ #
+ # This section we be called if authentication or re-authentication fails.
+ #
+ # You should remove any session information stored against
+ # `&Next-Pseudonym-Id`.
+ #
+ clear pseudonym {
+ ok
+ }
+
+ #
+ # ### `store session { ... }`
+ #
+ # If when sending a Reauthentication-Request a `&reply:Next-Reauth-Id`
+ # attribute is found, this section will be called.
+ #
+ # You should restore the contents of the following attributes using
+ # `&session-state:Session-ID` or `&reply:Next-Reauth-Id` as a key:
+ #
+ # - `&session-state:Counter`:: How many times this session has
+ # been resumed.
+ # - `&session-state:Session-Data`:: The master session key.
+ # - `&session-State:Permanent-Identity`::(optionally) the permanent
+ # identity of the user.
+ #
+ # If a failure rcode is returned, authentication continues but the
+ # Next-Reauth-Id will not be sent to the supplicant.
+ #
+ # NOTE: The cache module is ideally suited for storing session data.
+ # If you're using multiple instances of FreeRADIUS in a failover
+ # cluster, you may wish to use the redis or memcached drivers to
+ # provide a unified session store.
+ #
+ store session {
+ ok
+ }
+
+ #
+ # ### `load session { ... }`
+ #
+ # This section will be called prior to attempting re-authentication.
+ #
+ # You should restore the contents of the following attributes using
+ # `&session-state:Session-ID` or `&reply:Next-Reauth-Id` as a key:
+ #
+ # - `&session-state:Counter`:: How many times this session has
+ # been resumed.
+ # - `&session-state:Session-Data`:: The master session key.
+ # - `&session-State:Permanent-Identity`::(optionally) the permanent
+ # identity of the user.
+ #
+ # The following rcodes have special meanings in this section:
+ #
+ # - `reject` Reject the authentication attempt, send
+ # a Failure-Notification with an
+ # AT_NOTIFICATION value of 16384
+ # (General Failure).
+ # - `userlock` Reject the authentication attempt, send
+ # a Failure-Notification with an
+ # AT_NOTIFICATION value of 16384
+ # (General Failure).
+ # - `ok` Continue with reauthentication.
+ # - `updated` Continue with reauthentication.
+ # - Anything else Fall back to full authentication.
+ #
+ load session {
+ ok
+ }
+
+ #
+ # ### `clear session { ... }`
+ #
+ # This section we be called if authentication or re-authentication fails.
+ #
+ # You should remove any session information stored against
+ # `&Next-Reauth-Id`.
+ #
+ clear session {
+ ok
+ }
+}
# This section is run whenever the server needs to write an
# entry to the TLS session cache.
#
- # It should write the attribute &session-state:TLS-Session-Data
+ # It should write the attribute &session-state:Session-Data
# to the cache, along with any other attributes which
# need to be cached.
#
# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
# Version $Id$
#
-# Protocol dictionary for EAP-AKA
+# Protocol dictionary for EAP-AKA, EAP-AKA', and EAP-SIM
#
#
-PROTOCOL EAP-AKA 102
-BEGIN-PROTOCOL EAP-AKA
+PROTOCOL EAP-AKA-SIM 102
+BEGIN-PROTOCOL EAP-AKA-SIM
$INCLUDE dictionary.freeradius
$INCLUDE dictionary.rfc4187
$INCLUDE dictionary.3gpp-ts24.139
$INCLUDE dictionary.3gpp-ts24.302
-END-PROTOCOL EAP-AKA
+END-PROTOCOL EAP-AKA-SIM
#
# Skippable-Attributes
#
-ATTRIBUTE EAP-AKA-RQSI-Ind 142 octets
-ATTRIBUTE EAP-AKA-RQSI-Res 143 octets
+ATTRIBUTE RQSI-Ind 142 octets
+ATTRIBUTE RQSI-Res 143 octets
--- /dev/null
+# -*- text -*-
+# Copyright (C) 2019 The FreeRADIUS Server project and contributors
+# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
+# Version $Id$
+#
+# EAP-AKA attributes defined by 3gpp-ts24.302
+#
+
+#
+# Skippable-Attributes
+#
+ATTRIBUTE IPMS-Ind 137 octets
+ATTRIBUTE IPMS-Res 138 octets
+ATTRIBUTE Tust-Ind 139 octets
+ATTRIBUTE Short-Name-For-Network 140 octets
+ATTRIBUTE Full-Name-For-Network 141 octets
+
+ATTRIBUTE Twan-Conn-Mode 144 octets
--- /dev/null
+# -*- text -*-
+# Copyright (C) 2019 The FreeRADIUS Server project and contributors
+# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
+# Version $Id$
+#
+# Attributes needed by FreeRADIUS internally
+#
+
+#
+# Internal (Non-Protocol) attributes
+#
+FLAGS internal
+ATTRIBUTE Subtype 256 integer
+
+VALUE Subtype AKA-Challenge 1
+VALUE Subtype AKA-Authentication-Reject 2
+VALUE Subtype AKA-Synchronization-Failure 4
+VALUE Subtype AKA-Identity 5 # EAP-SIM uses SIM-Start packets for the same purpose
+VALUE Subtype SIM-Start 10
+VALUE Subtype SIM-Challenge 11
+VALUE Subtype AKA-SIM-Notification 12
+VALUE Subtype AKA-SIM-Reauthentication 13
+VALUE Subtype AKA-SIM-Client-Error 14
+
+ATTRIBUTE HMAC 257 string
+ATTRIBUTE Key 258 octets
+ATTRIBUTE Extra 259 octets
+
+#
+# UMTS vector
+#
+ATTRIBUTE XRES 260 octets
+ATTRIBUTE CK 261 octets
+ATTRIBUTE IK 262 octets
+ATTRIBUTE AUTHN 263 octets
+
+#
+# SIM vector
+#
+ATTRIBUTE SRES 270 octets
+ATTRIBUTE KC 271 octets
+
+#
+# Identity negotiation
+#
+ATTRIBUTE Method-Hint 280 integer
+
+VALUE Method-Hint AKA-Prime 0
+VALUE Method-Hint AKA 1
+VALUE Method-Hint SIM 2
+
+ATTRIBUTE Identity-Type 281 integer
+
+VALUE Identity-Type Permanent 0
+VALUE Identity-Type Pseudonym 1
+VALUE Identity-Type Fastauth 2
+
+ATTRIBUTE Permanent-Identity 282 string
+ATTRIBUTE KDF-Identity 283 string # Allow override of KDF Identity input
+
+#
+# Reauthentication
+#
+ATTRIBUTE MK 290 octets # Master key
+ATTRIBUTE AK 291 octets
+ATTRIBUTE K-Re 292 octets # AKA' KDF 1 reauthentication key.
+
+
--- /dev/null
+# -*- text -*-
+# Copyright (C) 2019 The FreeRADIUS Server project and contributors
+# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
+# Version $Id$
+##############################################################################
+#
+# EAP-SIM and EAP-AKA attributes
+#
+##############################################################################
+#
+# Non-Skippable attributes
+#
+ATTRIBUTE RAND 1 octets[16] array # Not an array, single value
+ATTRIBUTE AUTN 2 octets[16]
+ATTRIBUTE RES 3 octets[16] # Special case (length field with length in bits)
+ATTRIBUTE AUTS 4 octets[14] # Special case no reserved field.
+ATTRIBUTE Nonce-MT 7 octets[16]
+ATTRIBUTE Permanent-ID-Req 10 bool
+ATTRIBUTE MAC 11 octets[16]
+ATTRIBUTE Notification 12 short
+
+VALUE Notification General-Failure-After-Authentication 0
+VALUE Notification Temporarily-Denied 1026
+VALUE Notification Not-Subscribed 1031
+VALUE Notification General-Failure 16384
+VALUE Notification Success 32768
+
+ATTRIBUTE Any-ID-Req 13 bool
+ATTRIBUTE Identity 14 string
+ATTRIBUTE Version-List 15 short array
+ATTRIBUTE Selected-Version 16 short
+ATTRIBUTE Fullauth-ID-Req 17 bool
+ATTRIBUTE Client-Error-Code 22 short
+
+VALUE Client-Error-Code Unable-To-Process-Packet 0
+VALUE Client-Error-Code Unsupported-Version 1
+VALUE Client-Error-Code Insufficient-Challenges 2
+VALUE Client-Error-Code RANDS-Not-Fresh 3
+
+ATTRIBUTE KDF-Input 23 string
+ATTRIBUTE KDF 24 short
+
+VALUE KDF Prime-With-CK-Prime-IK-Prime 1
+
+#
+# Skippable-Attributes
+#
+ATTRIBUTE IV 129 octets[16]
+ATTRIBUTE Encr-Data 130 tlv encrypt=4
+
+#
+# These attributes are reversibly encrypted
+#
+BEGIN-TLV Encr-Data
+ATTRIBUTE Padding 6 octets # 4, 8, 12 bytes
+ATTRIBUTE Counter 19 short
+ATTRIBUTE Counter-Too-Small 20 bool
+ATTRIBUTE Nonce-S 21 octets[16]
+ATTRIBUTE Next-Pseudonym 132 string
+ATTRIBUTE Next-Reauth-ID 133 string
+END-TLV Encr-Data
+
+ATTRIBUTE Checkcode 134 octets # 0, 20 or 32 bytes
+
+ATTRIBUTE Bidding 136 short
+
+VALUE Bidding Prefer-AKA 0
+VALUE Bidding Prefer-AKA-Prime 32768 # D Bit
+
+ATTRIBUTE Result-Ind 137 bool
#
# Non-Skippable attributes
#
-ATTRIBUTE EAP-AKA-KDF-Input 23 string
-ATTRIBUTE EAP-AKA-KDF 24 short
+ATTRIBUTE KDF-Input 23 string
+ATTRIBUTE KDF 24 short
--- /dev/null
+# -*- text -*-
+# Copyright (C) 2019 The FreeRADIUS Server project and contributors
+# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
+# Version $Id$
+#
+# EAP-AKA attributes defined by rfc7458
+#
+
+#
+# Skippable-Attributes
+#
+ATTRIBUTE Virtual-Network-ID 145 octets
+ATTRIBUTE Virtual-Network-Req 146 octets
+ATTRIBUTE Connectivity-Type 147 octets
+ATTRIBUTE Handover-Indication 148 octets
+ATTRIBUTE Handover-Session-ID 149 octets
+ATTRIBUTE MN-Serial-ID 150 octets
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-#
-# EAP-AKA attributes defined by 3gpp-ts24.302
-#
-
-#
-# Skippable-Attributes
-#
-ATTRIBUTE EAP-AKA-IPMS-Ind 137 octets
-ATTRIBUTE EAP-AKA-IPMS-Res 138 octets
-ATTRIBUTE EAP-AKA-Tust-Ind 139 octets
-ATTRIBUTE EAP-AKA-Short-Name-For-Network 140 octets
-ATTRIBUTE EAP-AKA-Full-Name-For-Network 141 octets
-
-ATTRIBUTE EAP-AKA-Twan-Conn-Mode 144 octets
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-#
-# Attributes needed by FreeRADIUS internally
-#
-
-#
-# Internal (Non-Protocol) attributes
-#
-FLAGS internal
-ATTRIBUTE EAP-AKA-Subtype 256 integer
-
-VALUE EAP-AKA-Subtype AKA-Challenge 1
-VALUE EAP-AKA-Subtype AKA-Authentication-Reject 2
-VALUE EAP-AKA-Subtype AKA-Synchronization-Failure 4
-VALUE EAP-AKA-Subtype AKA-Identity 5
-VALUE EAP-AKA-Subtype AKA-Notification 12
-VALUE EAP-AKA-Subtype AKA-Reauthentication 13
-VALUE EAP-AKA-Subtype AKA-Client-Error 14
-
-ATTRIBUTE EAP-AKA-HMAC 257 string
-ATTRIBUTE EAP-AKA-Key 258 octets
-ATTRIBUTE EAP-AKA-Extra 259 octets
-
-ATTRIBUTE EAP-AKA-XRES 260 octets
-ATTRIBUTE EAP-AKA-CK 261 octets
-ATTRIBUTE EAP-AKA-IK 262 octets
-ATTRIBUTE EAP-AKA-AUTHN 263 octets
-
-#
-# Keys to store for fast-reauthentication
-#
-ATTRIBUTE EAP-AKA-MK 264 octets # Master key
-ATTRIBUTE EAP-AKA-AK 265 octets
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-##############################################################################
-#
-# EAP-AKA and EAP-AKA' Protocol attributes
-#
-##############################################################################
-#
-# Non-Skippable attributes
-#
-ATTRIBUTE EAP-AKA-RAND 1 octets[16] # Not an array, single value
-ATTRIBUTE EAP-AKA-AUTN 2 octets[16]
-ATTRIBUTE EAP-AKA-RES 3 octets[16] # Special case (length field with length in bits)
-ATTRIBUTE EAP-AKA-AUTS 4 octets[14] # Special case no reserved field.
-ATTRIBUTE EAP-AKA-Permanent-ID-Req 10 bool
-ATTRIBUTE EAP-AKA-MAC 11 octets[16]
-ATTRIBUTE EAP-AKA-Notification 12 short
-
-VALUE EAP-AKA-Notification General-Failure-After-Authentication 0
-VALUE EAP-AKA-Notification Temporarily-Denied 1026
-VALUE EAP-AKA-Notification Not-Subscribed 1031
-VALUE EAP-AKA-Notification General-Failure 16384
-VALUE EAP-AKA-Notification Success 32768
-
-ATTRIBUTE EAP-AKA-Any-ID-Req 13 bool
-ATTRIBUTE EAP-AKA-Identity 14 string
-
-ATTRIBUTE EAP-AKA-Fullauth-ID-Req 17 bool
-ATTRIBUTE EAP-AKA-Client-Error-Code 22 short
-
-VALUE EAP-AKA-Client-Error-Code Unable-To-Process-Packet 0
-VALUE EAP-AKA-Client-Error-Code Unsupported-Version 1
-VALUE EAP-AKA-Client-Error-Code Insufficient-Challenges 2
-VALUE EAP-AKA-Client-Error-Code RANDS-Not-Fresh 3
-
-ATTRIBUTE EAP-AKA-KDF-Input 23 string
-ATTRIBUTE EAP-AKA-KDF 24 short
-
-VALUE EAP-AKA-KDF EAP-AKA 0
-VALUE EAP-AKA-KDF EAP-AKA-Prime-With-CK-Prime-IK-Prime 1
-
-#
-# Skippable-Attributes
-#
-ATTRIBUTE EAP-AKA-IV 129 octets[16]
-ATTRIBUTE EAP-AKA-Encr-Data 130 tlv encrypt=4
-
-#
-# These attributes are reversibly encrypted
-#
-BEGIN-TLV EAP-AKA-Encr-Data
-ATTRIBUTE EAP-AKA-Padding 6 octets # 4, 8, 12 bytes
-ATTRIBUTE EAP-AKA-Counter 19 short
-ATTRIBUTE EAP-AKA-Counter-Too-Small 20 bool
-ATTRIBUTE EAP-AKA-Nonce-S 21 octets[16]
-ATTRIBUTE EAP-AKA-Next-Pseudonym 132 string
-ATTRIBUTE EAP-AKA-Next-Reauth-ID 133 string
-END-TLV EAP-AKA-Encr-Data
-
-ATTRIBUTE EAP-AKA-Checkcode 134 octets # 0, 20 or 32 bytes
-
-ATTRIBUTE EAP-AKA-Bidding 136 short
-
-VALUE EAP-AKA-Bidding Prefer-AKA 0
-VALUE EAP-AKA-Bidding Prefer-AKA-Prime 32768 # D Bit
-
-ATTRIBUTE EAP-AKA-Result-Ind 137 bool
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-#
-# EAP-AKA attributes defined by rfc7458
-#
-
-#
-# Skippable-Attributes
-#
-ATTRIBUTE EAP-AKA-Virtual-Network-ID 145 octets
-ATTRIBUTE EAP-AKA-Virtual-Network-Req 146 octets
-ATTRIBUTE EAP-AKA-Connectivity-Type 147 octets
-ATTRIBUTE EAP-AKA-Handover-Indication 148 octets
-ATTRIBUTE EAP-AKA-Handover-Session-ID 149 octets
-ATTRIBUTE EAP-AKA-MN-Serial-ID 150 octets
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-#
-# Protocol dictionary for EAP-SIM
-#
-#
-PROTOCOL EAP-SIM 101
-BEGIN-PROTOCOL EAP-SIM
-
-$INCLUDE dictionary.freeradius
-$INCLUDE dictionary.rfc4187
-$INCLUDE dictionary.rfc5448
-$INCLUDE dictionary.rfc7458
-$INCLUDE dictionary.3gpp-ts24.139
-$INCLUDE dictionary.3gpp-ts24.302
-
-END-PROTOCOL EAP-SIM
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-#
-# EAP-SIM attributes defined by 3gpp-ts24.139
-#
-
-#
-# Skippable-Attributes
-#
-ATTRIBUTE EAP-SIM-RQSI-Ind 142 octets
-ATTRIBUTE EAP-SIM-RQSI-Res 143 octets
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-#
-# EAP-SIM attributes defined by 3gpp-ts24.302
-#
-
-#
-# Skippable-Attributes
-#
-ATTRIBUTE EAP-SIM-IPMS-Ind 137 octets
-ATTRIBUTE EAP-SIM-IPMS-Res 138 octets
-ATTRIBUTE EAP-SIM-Tust-Ind 139 octets
-ATTRIBUTE EAP-SIM-Short-Name-For-Network 140 octets
-ATTRIBUTE EAP-SIM-Full-Name-For-Network 141 octets
-
-ATTRIBUTE EAP-SIM-Twan-Conn-Mode 144 octets
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-#
-# Attributes needed by FreeRADIUS internally
-#
-
-#
-# Internal (Non-Protocol) attributes
-#
-FLAGS internal
-ATTRIBUTE EAP-SIM-Subtype 256 integer
-
-VALUE EAP-SIM-Subtype SIM-Start 10
-VALUE EAP-SIM-Subtype SIM-Challenge 11
-VALUE EAP-SIM-Subtype SIM-Notification 12
-VALUE EAP-SIM-Subtype SIM-Reauthentication 13
-VALUE EAP-SIM-Subtype SIM-Client-Error 14
-
-ATTRIBUTE EAP-SIM-HMAC 257 string
-ATTRIBUTE EAP-SIM-Key 258 octets
-ATTRIBUTE EAP-SIM-Extra 259 octets
-
-ATTRIBUTE EAP-SIM-MK 264 octets # Master key
-
-ATTRIBUTE EAP-SIM-SRES 1222 octets
-ATTRIBUTE EAP-SIM-KC 1223 octets
-ATTRIBUTE EAP-SIM-State 1224 integer
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-##############################################################################
-#
-# EAP-SIM attributes defined by rfc4187
-#
-##############################################################################
-
-#
-# Non-Skippable attributes
-#
-ATTRIBUTE EAP-SIM-RAND 1 octets[16] array
-ATTRIBUTE EAP-SIM-Nonce-MT 7 octets[16]
-ATTRIBUTE EAP-SIM-Permanent-ID-Req 10 bool
-ATTRIBUTE EAP-SIM-MAC 11 octets[16]
-ATTRIBUTE EAP-SIM-Notification 12 short
-
-VALUE EAP-SIM-Notification General-Failure-After-Authentication 0
-VALUE EAP-SIM-Notification Temporarily-Denied 1026
-VALUE EAP-SIM-Notification Not-Subscribed 1031
-VALUE EAP-SIM-Notification General-Failure 16384
-VALUE EAP-SIM-Notification Success 32768
-
-ATTRIBUTE EAP-SIM-Any-ID-Req 13 bool
-ATTRIBUTE EAP-SIM-Identity 14 string
-ATTRIBUTE EAP-SIM-Version-List 15 short array
-ATTRIBUTE EAP-SIM-Selected-Version 16 short
-ATTRIBUTE EAP-SIM-Fullauth-ID-Req 17 bool
-ATTRIBUTE EAP-SIM-Client-Error-Code 22 short
-
-VALUE EAP-SIM-Client-Error-Code Unable-To-Process-Packet 0
-VALUE EAP-SIM-Client-Error-Code Unsupported-Version 1
-VALUE EAP-SIM-Client-Error-Code Insufficient-Challenges 2
-VALUE EAP-SIM-Client-Error-Code RANDS-Not-Fresh 3
-
-#
-# Skippable-Attributes
-#
-ATTRIBUTE EAP-SIM-IV 129 octets[16]
-ATTRIBUTE EAP-SIM-Encr-Data 130 tlv encrypt=4
-
-#
-# These attributes are reversibly encrypted
-#
-BEGIN-TLV EAP-SIM-Encr-Data
-ATTRIBUTE EAP-SIM-Padding 6 octets # 4, 8, 12 bytes length
-ATTRIBUTE EAP-SIM-Counter 19 short
-ATTRIBUTE EAP-SIM-Counter-Too-Small 20 bool
-ATTRIBUTE EAP-SIM-Nonce-S 21 octets[16]
-ATTRIBUTE EAP-SIM-Next-Pseudonym 132 string
-ATTRIBUTE EAP-SIM-Next-Reauth-ID 133 string
-END-TLV EAP-SIM-Encr-Data
-
-ATTRIBUTE EAP-SIM-Result-Ind 137 bool
-
-#
-# Internal attributes used by FreeRADIUS
-#
-FLAGS internal
-ATTRIBUTE EAP-SIM-Subtype 256 integer
-
-VALUE EAP-SIM-Subtype SIM-Start 10
-VALUE EAP-SIM-Subtype SIM-Challenge 11
-VALUE EAP-SIM-Subtype SIM-Notification 12
-VALUE EAP-SIM-Subtype SIM-Reauthentication 13
-VALUE EAP-SIM-Subtype SIM-Client-Error 14
-
-ATTRIBUTE EAP-SIM-SRES 1222 octets
-ATTRIBUTE EAP-SIM-KC 1223 octets
-ATTRIBUTE EAP-SIM-State 1224 integer
-
-#
-# Keys to store for fast-reauthentication
-#
-ATTRIBUTE EAP-AKA-MK 264 octets # Master key
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-#
-# EAP-SIM attributes defined by rfc5448
-#
-
-#
-# Non-Skippable attributes
-#
-ATTRIBUTE EAP-SIM-KDF-Input 24 octets
-ATTRIBUTE EAP-SIM-KDF 25 octets
+++ /dev/null
-# -*- text -*-
-# Copyright (C) 2019 The FreeRADIUS Server project and contributors
-# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
-# Version $Id$
-#
-# EAP-SIM attributes defined by rfc7458
-#
-
-#
-# Skippable-Attributes
-#
-ATTRIBUTE EAP-SIM-Virtual-Network-ID 145 octets
-ATTRIBUTE EAP-SIM-Virtual-Network-Req 146 octets
-ATTRIBUTE EAP-SIM-Connectivity-Type 147 octets
-ATTRIBUTE EAP-SIM-Handover-Indication 148 octets
-ATTRIBUTE EAP-SIM-Handover-Session-ID 149 octets
-ATTRIBUTE EAP-SIM-MN-Serial-ID 150 octets
ATTRIBUTE LDAP-Sync-Entry-UUID 8 octets
END-TLV LDAP-Sync-TLV
-#
-# Range: 1200-1210
-#
-# SIM attributes. These do not belong to either EAP-AKA or EAP-SIM
-# they represent attributes of a SIM card.
-#
-
-
#
# Server-side "listen type = foo"
#
# The algorithm the SIM card uses (Milenage for UMTS, COMP128 for others)
ATTRIBUTE SIM-Algo-Version 5206 integer
-VALUE SIM-Algo-Version Comp128v1 0
-VALUE SIM-Algo-Version Comp128v2 1
-VALUE SIM-Algo-Version Comp128v3 2
-VALUE SIM-Algo-Version Milenage 3
+VALUE SIM-Algo-Version Comp128-1 1
+VALUE SIM-Algo-Version Comp128-2 2
+VALUE SIM-Algo-Version Comp128-3 3
+VALUE SIM-Algo-Version Comp128-4 4 # GSM Milenage TS.55.205
+VALUE SIM-Algo-Version Milenage 5 # UMTS Milenage
+VALUE SIM-Algo-Version TS-34-108-UMTS 7 # UMTS Test algorithm
ATTRIBUTE SIM-Method-Hint 5207 integer
--- /dev/null
+TARGET := libfreeradius-eap-aka-sim.a
+
+
+SOURCES := \
+ base.c \
+ state_machine.c \
+ crypto.c \
+ decode.c \
+ encode.c \
+ fips186prf.c \
+ id.c \
+ vector.c \
+ xlat.c
+
+TGT_PREREQS := $(LIBFREERADIUS_SERVER) libfreeradius-eap.a libfreeradius-util.a libfreeradius-sim.a
--- /dev/null
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file src/lib/aka-sim/attrs.h
+ * @brief Attributes to EAP-SIM/AKA/AKA' clients and servers.
+ *
+ * @copyright 2003-2016 The FreeRADIUS server project
+ */
+#include <freeradius-devel/server/rad_assert.h>
+#include <freeradius-devel/util/dict.h>
+#include <freeradius-devel/protocol/eap/aka-sim/dictionary.h>
+#include <freeradius-devel/protocol/eap/aka-sim/rfc4187.h>
+#include <freeradius-devel/protocol/eap/aka-sim/dictionary.h>
+#include <freeradius-devel/protocol/eap/aka-sim/rfc4187.h>
+#include <freeradius-devel/protocol/eap/aka-sim/freeradius.h>
+
+extern fr_dict_t *dict_freeradius;
+extern fr_dict_t *dict_radius;
+extern fr_dict_t *dict_eap_aka_sim;
+
+extern fr_dict_attr_t const *attr_eap_aka_sim_ak;
+extern fr_dict_attr_t const *attr_eap_aka_sim_any_id_req;
+extern fr_dict_attr_t const *attr_eap_aka_sim_autn;
+extern fr_dict_attr_t const *attr_eap_aka_sim_auts;
+extern fr_dict_attr_t const *attr_eap_aka_sim_bidding;
+extern fr_dict_attr_t const *attr_eap_aka_sim_checkcode;
+extern fr_dict_attr_t const *attr_eap_aka_sim_ck;
+extern fr_dict_attr_t const *attr_eap_aka_sim_client_error_code;
+extern fr_dict_attr_t const *attr_eap_aka_sim_counter_too_small;
+extern fr_dict_attr_t const *attr_eap_aka_sim_counter;
+extern fr_dict_attr_t const *attr_eap_aka_sim_encr_data;
+extern fr_dict_attr_t const *attr_eap_aka_sim_fullauth_id_req;
+extern fr_dict_attr_t const *attr_eap_aka_sim_identity_type;
+extern fr_dict_attr_t const *attr_eap_aka_sim_identity;
+extern fr_dict_attr_t const *attr_eap_aka_sim_ik;
+extern fr_dict_attr_t const *attr_eap_aka_sim_iv;
+extern fr_dict_attr_t const *attr_eap_aka_sim_k_re;
+extern fr_dict_attr_t const *attr_eap_aka_sim_kc;
+extern fr_dict_attr_t const *attr_eap_aka_sim_kdf_identity;
+extern fr_dict_attr_t const *attr_eap_aka_sim_kdf_input;
+extern fr_dict_attr_t const *attr_eap_aka_sim_kdf;
+extern fr_dict_attr_t const *attr_eap_aka_sim_mac;
+extern fr_dict_attr_t const *attr_eap_aka_sim_method_hint;
+extern fr_dict_attr_t const *attr_eap_aka_sim_mk;
+extern fr_dict_attr_t const *attr_eap_aka_sim_next_pseudonym;
+extern fr_dict_attr_t const *attr_eap_aka_sim_next_reauth_id;
+extern fr_dict_attr_t const *attr_eap_aka_sim_nonce_mt;
+extern fr_dict_attr_t const *attr_eap_aka_sim_nonce_s;
+extern fr_dict_attr_t const *attr_eap_aka_sim_notification;
+extern fr_dict_attr_t const *attr_eap_aka_sim_padding;
+extern fr_dict_attr_t const *attr_eap_aka_sim_permanent_id_req;
+extern fr_dict_attr_t const *attr_eap_aka_sim_permanent_identity;
+extern fr_dict_attr_t const *attr_eap_aka_sim_rand;
+extern fr_dict_attr_t const *attr_eap_aka_sim_res;
+extern fr_dict_attr_t const *attr_eap_aka_sim_result_ind;
+extern fr_dict_attr_t const *attr_eap_aka_sim_sres;
+extern fr_dict_attr_t const *attr_eap_aka_sim_subtype;
+extern fr_dict_attr_t const *attr_eap_aka_sim_selected_version;
+extern fr_dict_attr_t const *attr_eap_aka_sim_version_list;
+extern fr_dict_attr_t const *attr_eap_aka_sim_xres;
+
+
+extern fr_dict_attr_t const *attr_ms_mppe_recv_key;
+extern fr_dict_attr_t const *attr_ms_mppe_send_key;
+
+extern fr_dict_attr_t const *attr_eap_identity;
+extern fr_dict_attr_t const *attr_eap_type;
+extern fr_dict_attr_t const *attr_session_data;
+extern fr_dict_attr_t const *attr_session_id;
+extern fr_dict_attr_t const *attr_sim_algo_version;
+extern fr_dict_attr_t const *attr_sim_amf;
+extern fr_dict_attr_t const *attr_sim_ki;
+extern fr_dict_attr_t const *attr_sim_op;
+extern fr_dict_attr_t const *attr_sim_opc;
+extern fr_dict_attr_t const *attr_sim_sqn;
--- /dev/null
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file src/lib/aka-sim/base.c
+ * @brief Code common to EAP-SIM/AKA/AKA' clients and servers.
+ *
+ * The development of the EAP-SIM support was funded by Internet Foundation
+ * Austria (http://www.nic.at/ipa).
+ *
+ * @copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
+ * @copyright 2003-2016 The FreeRADIUS server project
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/util/base.h>
+#include <freeradius-devel/util/sha1.h>
+
+#include <freeradius-devel/server/rad_assert.h>
+#include <freeradius-devel/server/module.h>
+
+#include <freeradius-devel/tls/base.h>
+
+#include <freeradius-devel/eap/types.h>
+
+#include <freeradius-devel/eap_aka_sim/base.h>
+#include <freeradius-devel/eap_aka_sim/attrs.h>
+
+static int instance_count = 0;
+
+fr_dict_t *dict_freeradius;
+fr_dict_t *dict_radius;
+fr_dict_t *dict_eap_aka_sim;
+
+extern fr_dict_autoload_t libfreeradius_aka_sim_dict[];
+fr_dict_autoload_t libfreeradius_aka_sim_dict[] = {
+ { .out = &dict_freeradius, .proto = "freeradius" },
+ { .out = &dict_radius, .proto = "radius" },
+ { .out = &dict_eap_aka_sim, .base_dir = "eap/aka-sim", .proto = "eap-aka-sim" },
+
+ { NULL }
+};
+
+fr_dict_attr_t const *attr_eap_aka_sim_ak;
+fr_dict_attr_t const *attr_eap_aka_sim_any_id_req;
+fr_dict_attr_t const *attr_eap_aka_sim_autn;
+fr_dict_attr_t const *attr_eap_aka_sim_auts;
+fr_dict_attr_t const *attr_eap_aka_sim_bidding;
+fr_dict_attr_t const *attr_eap_aka_sim_checkcode;
+fr_dict_attr_t const *attr_eap_aka_sim_ck;
+fr_dict_attr_t const *attr_eap_aka_sim_client_error_code;
+fr_dict_attr_t const *attr_eap_aka_sim_counter_too_small;
+fr_dict_attr_t const *attr_eap_aka_sim_counter;
+fr_dict_attr_t const *attr_eap_aka_sim_encr_data;
+fr_dict_attr_t const *attr_eap_aka_sim_fullauth_id_req;
+fr_dict_attr_t const *attr_eap_aka_sim_identity_type;
+fr_dict_attr_t const *attr_eap_aka_sim_identity;
+fr_dict_attr_t const *attr_eap_aka_sim_ik;
+fr_dict_attr_t const *attr_eap_aka_sim_iv;
+fr_dict_attr_t const *attr_eap_aka_sim_k_re;
+fr_dict_attr_t const *attr_eap_aka_sim_kc;
+fr_dict_attr_t const *attr_eap_aka_sim_kdf_identity;
+fr_dict_attr_t const *attr_eap_aka_sim_kdf_input;
+fr_dict_attr_t const *attr_eap_aka_sim_kdf;
+fr_dict_attr_t const *attr_eap_aka_sim_mac;
+fr_dict_attr_t const *attr_eap_aka_sim_method_hint;
+fr_dict_attr_t const *attr_eap_aka_sim_mk;
+fr_dict_attr_t const *attr_eap_aka_sim_next_pseudonym;
+fr_dict_attr_t const *attr_eap_aka_sim_next_reauth_id;
+fr_dict_attr_t const *attr_eap_aka_sim_nonce_mt;
+fr_dict_attr_t const *attr_eap_aka_sim_nonce_s;
+fr_dict_attr_t const *attr_eap_aka_sim_notification;
+fr_dict_attr_t const *attr_eap_aka_sim_padding;
+fr_dict_attr_t const *attr_eap_aka_sim_permanent_id_req;
+fr_dict_attr_t const *attr_eap_aka_sim_permanent_identity;
+fr_dict_attr_t const *attr_eap_aka_sim_rand;
+fr_dict_attr_t const *attr_eap_aka_sim_res;
+fr_dict_attr_t const *attr_eap_aka_sim_result_ind;
+fr_dict_attr_t const *attr_eap_aka_sim_sres;
+fr_dict_attr_t const *attr_eap_aka_sim_selected_version;
+fr_dict_attr_t const *attr_eap_aka_sim_subtype;
+fr_dict_attr_t const *attr_eap_aka_sim_version_list;
+fr_dict_attr_t const *attr_eap_aka_sim_xres;
+
+fr_dict_attr_t const *attr_ms_mppe_recv_key;
+fr_dict_attr_t const *attr_ms_mppe_send_key;
+
+fr_dict_attr_t const *attr_eap_identity;
+fr_dict_attr_t const *attr_eap_type;
+fr_dict_attr_t const *attr_session_data;
+fr_dict_attr_t const *attr_session_id;
+fr_dict_attr_t const *attr_sim_algo_version;
+fr_dict_attr_t const *attr_sim_amf;
+fr_dict_attr_t const *attr_sim_ki;
+fr_dict_attr_t const *attr_sim_op;
+fr_dict_attr_t const *attr_sim_opc;
+fr_dict_attr_t const *attr_sim_sqn;
+
+extern fr_dict_attr_autoload_t libfreeradius_aka_sim_dict_attr[];
+fr_dict_attr_autoload_t libfreeradius_aka_sim_dict_attr[] = {
+ { .out = &attr_eap_aka_sim_ak, .name = "AK", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_any_id_req, .name = "Any-ID-Req", .type = FR_TYPE_BOOL, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_autn, .name = "AUTN", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_auts, .name = "AUTS", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_bidding, .name = "Bidding", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_checkcode, .name = "Checkcode", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_ck, .name = "CK", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_client_error_code, .name = "Client-Error-Code", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_counter_too_small, .name = "Counter-Too-Small", .type = FR_TYPE_BOOL, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_counter, .name = "Counter", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_encr_data, .name = "Encr-Data", .type = FR_TYPE_TLV, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_fullauth_id_req, .name = "Fullauth-ID-Req", .type = FR_TYPE_BOOL, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_identity_type, .name = "Identity-Type", .type = FR_TYPE_UINT32, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_identity, .name = "Identity", .type = FR_TYPE_STRING, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_ik, .name = "IK", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_iv, .name = "IV", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_k_re, .name = "K-Re", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_kc, .name = "KC", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_kdf_identity, .name = "KDF-Identity", .type = FR_TYPE_STRING, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_kdf_input, .name = "KDF-Input", .type = FR_TYPE_STRING, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_kdf, .name = "KDF", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_mac, .name = "MAC", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_method_hint, .name = "Method-Hint", .type = FR_TYPE_UINT32, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_mk, .name = "MK", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_next_pseudonym, .name = "Next-Pseudonym", .type = FR_TYPE_STRING, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_next_reauth_id, .name = "Next-Reauth-ID", .type = FR_TYPE_STRING, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_nonce_mt, .name = "Nonce-MT", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_nonce_s, .name = "Nonce-S", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_notification, .name = "Notification", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_padding, .name = "Padding", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_permanent_id_req, .name = "Permanent-Id-Req", .type = FR_TYPE_BOOL, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_permanent_identity, .name = "Permanent-Identity", .type = FR_TYPE_STRING, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_rand, .name = "RAND", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_res, .name = "RES", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_result_ind, .name = "Result-Ind", .type = FR_TYPE_BOOL, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_sres, .name = "RES", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_selected_version, .name = "Selected-Version", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_subtype, .name = "Subtype", .type = FR_TYPE_UINT32, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_version_list, .name = "Version-List", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_xres, .name = "XRES", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+
+ { .out = &attr_ms_mppe_send_key, .name = "MS-MPPE-Send-Key", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
+ { .out = &attr_ms_mppe_recv_key, .name = "MS-MPPE-Recv-Key", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
+
+ /*
+ * Separate from the EAP-AKA-AND-SIM dictionary
+ * as they're outside the notional numberspace.
+ */
+ { .out = &attr_eap_identity, .name = "EAP-Identity", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
+ { .out = &attr_eap_type, .name = "EAP-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
+
+ { .out = &attr_session_data, .name = "Session-Data", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_session_id, .name = "Session-Id", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sim_algo_version, .name = "SIM-Algo-Version", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
+ { .out = &attr_sim_amf, .name = "SIM-AMF", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sim_ki, .name = "SIM-Ki", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sim_op, .name = "SIM-OP", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sim_opc, .name = "SIM-OPc", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sim_sqn, .name = "SIM-SQN", .type = FR_TYPE_UINT64, .dict = &dict_freeradius },
+ { NULL }
+};
+
+/** SIM AT on-the-wire format attribute sizes
+ *
+ * Holds the min/max sizes of all supported SIM AT attribute values as they
+ * would be found in a SIM AT packet.
+ *
+ * These sizes may be different than the sizes of INTERNAL formats, PRESENTATION
+ * formats and generic NETWORK formats.
+ */
+size_t const fr_aka_sim_attr_sizes[FR_TYPE_MAX + 1][2] = {
+ [FR_TYPE_INVALID] = {~0, 0},
+
+ [FR_TYPE_STRING] = {0, ~0},
+ [FR_TYPE_OCTETS] = {0, ~0},
+
+ [FR_TYPE_BOOL] = {2, 2},
+ [FR_TYPE_UINT8] = {1, 1},
+ [FR_TYPE_UINT16] = {2, 2},
+ [FR_TYPE_UINT32] = {4, 4},
+ [FR_TYPE_UINT64] = {8, 8},
+
+ [FR_TYPE_TLV] = {2, ~0},
+
+ [FR_TYPE_MAX] = {~0, 0} //!< Ensure array covers all types.
+};
+
+/** Return the on-the-wire length of an attribute value
+ *
+ * @param[in] vp to return the length of.
+ * @return the length of the attribute.
+ */
+size_t fr_aka_sim_attr_len(VALUE_PAIR const *vp)
+{
+ switch (vp->vp_type) {
+ case FR_TYPE_VARIABLE_SIZE:
+ return vp->vp_length;
+
+ default:
+ return fr_aka_sim_attr_sizes[vp->vp_type][0];
+
+ case FR_TYPE_STRUCTURAL:
+ if (!fr_cond_assert(0)) return 0;
+ }
+}
+
+/** Return the number of bytes before the octets value
+ *
+ */
+size_t fr_aka_sim_octets_prefix_len(fr_dict_attr_t const *da)
+{
+ if (da->flags.array) return 0; /* Array elements have no padding */
+ if (!da->flags.length) return 2; /* Variable length attributes need length field */
+ if (!(da->flags.length % 4)) return 2; /* Values that are multiples of four have 2 reserved bytes */
+ return 0; /* Everything else has zero padding bytes */
+}
+
+int fr_aka_sim_init(void)
+{
+ if (instance_count > 0) {
+ instance_count++;
+ return 0;
+ }
+
+ if (fr_dict_autoload(libfreeradius_aka_sim_dict) < 0) {
+ PERROR("Failed loading libfreeradius-eap-aka-sim dictionaries");
+ return -1;
+ }
+ if (fr_dict_attr_autoload(libfreeradius_aka_sim_dict_attr) < 0) {
+ PERROR("Failed loading libfreeradius-eap-aka-sim attributes");
+ fr_dict_autofree(libfreeradius_aka_sim_dict);
+ return -1;
+ }
+ instance_count++;
+
+ return 0;
+}
+
+void fr_aka_sim_free(void)
+{
+ if (--instance_count > 0) return;
+
+ fr_dict_autofree(libfreeradius_aka_sim_dict);
+}
+
--- /dev/null
+#pragma once
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file src/lib/aka-sim/base.h
+ * @brief Functions common to SIM protocols (EAP-SIM/EAP-AKA/EAP-AKA')
+ *
+ * The development of the EAP/SIM support was funded by Internet Foundation
+ * Austria (http://www.nic.at/ipa).
+ *
+ * @copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
+ * @copyright 2003-2016 The FreeRADIUS server project
+ */
+RCSIDH(sim_h, "$Id$")
+
+#include <assert.h>
+#include <openssl/evp.h>
+#include <freeradius-devel/eap/compose.h>
+#include <freeradius-devel/eap/types.h>
+
+#include "id.h"
+
+#define AKA_SIM_MAX_STRING_LENGTH 1016 //!< Maximum size of a SIM/AKA['] string ((4 * 255) - 4).
+#define AKA_SIM_IV_SIZE 16 //!< Length of the IV used when processing AT_ENCR.
+#define AKA_SIM_MAC_DIGEST_SIZE 16 //!< Length of MAC used to prevent packet modification.
+#define AKA_SIM_MAC_SIZE 20 //!< Length of MAC used to prevent packet modification.
+#define AKA_SIM_AUTH_SIZE 16
+#define AKA_SIM_SQN_AK_SIZE 6
+#define AKA_SIM_NONCE_S_SIZE 16 //!< Length of re-authentication nonce
+
+#define AKA_SIM_KI_SIZE 16 //!< Length of secret data shared between SIM and AuC.
+#define AKA_SIM_OP_SIZE 16 //!< Length of Operator Algorithm Configuration.
+#define AKA_SIM_OPC_SIZE 16 //!< Length of modified Operator Algorithm Configuration.
+
+#define AKA_SIM_MK_SIZE 20 //!< Master key size
+#define AKA_SIM_K_RE_SIZE 32 //!< Reauthentication key size.
+
+#define AKA_SIM_SKIPPABLE_MAX 127 //!< The last non-skippable attribute.
+
+#define AKA_SIM_VECTOR_GSM_RAND_SIZE 16 //!< Length of RAND in GSM triplet.
+#define AKA_SIM_VECTOR_GSM_SRES_SIZE 4 //!< Length of SRES in GSM triplet.
+#define AKA_SIM_VECTOR_GSM_KC_SIZE 8 //!< Length of Kc in GSM triplet.
+
+#define AKA_SIM_VECTOR_UMTS_AUTN_SIZE 16
+#define AKA_SIM_VECTOR_UMTS_CK_SIZE 16
+#define AKA_SIM_VECTOR_UMTS_IK_SIZE 16
+#define AKA_SIM_VECTOR_UMTS_AK_SIZE 6
+#define AKA_SIM_VECTOR_UMTS_RAND_SIZE 16
+#define AKA_SIM_VECTOR_UMTS_XRES_MAX_SIZE 16
+
+#define EAP_SIM_VERSION 1
+#define EAP_SIM_NONCE_MT_SIZE 16 //!< Length of challenge from SIM client.
+
+#define EAP_AKA_SIM_AUTH_SIZE 16
+
+
+#define EAP_AKA_AUTS_SIZE 14 //!< Server sequence number. SIM checks this
+ //!< is within the correct range.
+#define EAP_AKA_AUTH_SIZE 16
+#define EAP_AKA_PRIME_AUTH_SIZE 32
+
+/** The type of auth vector held by a fr_aka_sim_keys_t
+ */
+typedef enum {
+ AKA_SIM_VECTOR_NONE = 0,
+ AKA_SIM_VECTOR_GSM, //!< Vector is GSM triplets.
+ AKA_SIM_VECTOR_UMTS, //!< Vector is UMTS quintuplets.
+ AKA_SIM_VECTOR_UMTS_REAUTH_KDF_0_REAUTH,
+ AKA_SIM_VECTOR_UMTS_REAUTH_KDF_1_REAUTH
+} fr_aka_sim_vector_type_t;
+
+/** Where to get EAP-SIM vectors from
+ */
+typedef enum {
+ AKA_SIM_VECTOR_SRC_AUTO, //!< Discover where to get Triplets from automatically.
+ AKA_SIM_VECTOR_SRC_TRIPLETS, //!< Source of triplets is EAP-SIM-* attributes.
+ AKA_SIM_VECTOR_SRC_QUINTUPLETS, //!< Source of triplets is derived from EAP-AKA-*
+ ///< quintuplets.
+ AKA_SIM_VECTOR_SRC_KI, //!< Should generate triplets locally using a Ki.
+ AKA_SIM_VECTOR_SRC_REAUTH, //!< Not a vector. Result of running KDF 0 reauth
+ ///< function.
+} fr_aka_sim_vector_src_t;
+
+typedef struct {
+ uint8_t rand[AKA_SIM_VECTOR_GSM_RAND_SIZE]; //!< RAND challenge to the SIM.
+ union {
+ uint8_t sres[AKA_SIM_VECTOR_GSM_SRES_SIZE]; //!< Signing response.
+ uint32_t sres_uint32;
+ };
+
+ union {
+ uint8_t kc[AKA_SIM_VECTOR_GSM_KC_SIZE]; //!< Keying response.
+ uint64_t kc_uint64;
+ };
+} fr_aka_sim_vector_gsm_t;
+
+typedef struct {
+ uint8_t autn[AKA_SIM_VECTOR_UMTS_AUTN_SIZE]; //!< Authentication vector from the AuC.
+ uint8_t ck[AKA_SIM_VECTOR_UMTS_CK_SIZE]; //!< Ciphering key.
+ uint8_t ik[AKA_SIM_VECTOR_UMTS_IK_SIZE]; //!< Integrity key.
+ uint8_t ak[AKA_SIM_VECTOR_UMTS_AK_SIZE]; //!< Anonymity key.
+ uint8_t rand[AKA_SIM_VECTOR_UMTS_RAND_SIZE]; //!< RAND challenge to the SIM.
+ uint8_t xres[AKA_SIM_VECTOR_UMTS_XRES_MAX_SIZE];//!< Signing response.
+ size_t xres_len; //!< Length of res (it's variable).
+} fr_aka_sim_vector_umts_t;
+
+/** Stores our checkcode state
+ *
+ * The checkcode is a hash of all identity packets exchanged
+ * up until the challenge is sent.
+ *
+ * It allows both parties to verify that they've seen the same
+ * sequence of packets.
+ */
+typedef struct {
+ EVP_MD_CTX *md_ctx; //!< Context to hold state of digest as we
+ ///< consume packets.
+} fr_aka_sim_checkcode_t;
+
+/** Master key state struct for all SIMlike EAP protocols
+ *
+ */
+typedef struct {
+ /*
+ * Inputs
+ */
+ uint8_t *identity; //!< Identity from AT_IDENTITY.
+ size_t identity_len; //!< Length of the identity.
+
+ uint8_t *network; //!< Network name (EAP-AKA-Prime only).
+ size_t network_len; //!< Length of the network name (EAP-AKA-Prime only).
+
+ uint64_t sqn; //!< Sequence number
+
+ struct {
+ /*
+ * Authentication vectors from HLR or local AuC
+ */
+ union {
+ /** Input to kdf_0_gsm
+ */
+ struct {
+ fr_aka_sim_vector_gsm_t vector[3]; //!< GSM vectors.
+ uint32_t num_vectors; //!< Number of input vectors
+ //!< we're using (2 or 3).
+
+ uint8_t nonce_mt[EAP_SIM_NONCE_MT_SIZE];//!< Nonce provided by the client.
+ uint8_t version_list[FR_MAX_STRING_LEN];//!< Version list from negotiation.
+ uint8_t version_list_len; //!< Length of version list.
+ uint8_t version_select[2]; //!< Version we agreed.
+ } gsm;
+
+ /** Input to kdf_*_umts
+ */
+ struct {
+ fr_aka_sim_vector_umts_t vector; //!< UMTS vector.
+ uint16_t kdf_selected;
+ } umts;
+
+ /** Input/output from kdf_*_reauth_*
+ */
+ struct {
+ uint16_t counter; //!< Re-authentication counter.
+ uint8_t nonce_s[AKA_SIM_NONCE_S_SIZE]; //!< Re-authentication challenge.
+ uint8_t xkey_prime[20]; //!< Derived reauthentication key
+ //!< for AKA (kdf 0).
+ } reauth;
+ };
+
+ /*
+ * Original ki and OPc to allow AUTS validation
+ */
+ struct {
+ uint8_t ki[AKA_SIM_KI_SIZE]; //!< Secret shared between AuC and SIM.
+ uint8_t opc[AKA_SIM_OPC_SIZE]; //!< Operator algorithm input.
+ } auc;
+
+ fr_aka_sim_vector_type_t vector_type; //!< What type of authentication vector
+ //!< we're using to authenticate the SIM.
+ fr_aka_sim_vector_src_t vector_src; //!< Where the vector came from.
+ };
+
+ /*
+ * Inputs/outputs
+ */
+ uint8_t mk[AKA_SIM_MK_SIZE]; //!< Master key from session attributes.
+ size_t mk_len;
+ uint8_t k_re[AKA_SIM_K_RE_SIZE]; //!< Derived reauthentication key for AKA'(kdf 1).
+
+ /*
+ * Intermediates
+ */
+ uint8_t ck_prime[AKA_SIM_VECTOR_UMTS_CK_SIZE]; //!< Derived from CK, for AKA'.
+ uint8_t ik_prime[AKA_SIM_VECTOR_UMTS_IK_SIZE]; //!< Derived from IK, for AKA'.
+
+ /*
+ * Outputs
+ */
+ uint8_t k_aut[32]; //!< Derived authentication key.
+ size_t k_aut_len; //!< Length of k_aut. 16 for AKA/SIM, 32 for AKA'.
+ uint8_t k_encr[16]; //!< Derived encryption key.
+ uint8_t msk[64]; //!< Derived master session key.
+ uint8_t emsk[64]; //!< Derived extended master session key.
+} fr_aka_sim_keys_t;
+
+typedef struct {
+ fr_aka_sim_keys_t const *keys; //!< From the EAP session.
+ uint8_t iv[AKA_SIM_IV_SIZE]; //!< From the current packet.
+ bool have_iv; //!< Whether we found the IV already.
+} fr_aka_sim_decode_ctx_t;
+
+typedef struct {
+ fr_dict_attr_t const *root; //!< Root attribute of the dictionary.
+ fr_aka_sim_keys_t const *keys; //!< From the EAP session.
+ uint8_t iv[AKA_SIM_IV_SIZE]; //!< Generated by us using our PRNG.
+ bool iv_included; //!< Whether we've already added an IV to this packet.
+
+ /*
+ * Additional HMAC inputs
+ */
+ EVP_MD const *hmac_md; //!< HMAC digest algorithm, usually EVP_sha1().
+ eap_packet_t *eap_packet; //!< Needed for HMAC generation so we can construct
+ ///< the EAP packet header.
+ uint8_t const *hmac_extra; //!< Extra data for the HMAC function.
+ size_t hmac_extra_len; //!< The length of the HMAC data.
+} fr_aka_sim_encode_ctx_t;
+
+extern size_t const fr_aka_sim_attr_sizes[FR_TYPE_MAX + 1][2];
+
+/*
+ * decode.c
+ */
+ssize_t fr_aka_sim_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_t const *dict,
+ uint8_t const *data, size_t data_len, void *decoder_ctx);
+
+int fr_aka_sim_decode(REQUEST *request, fr_cursor_t *decoded, fr_dict_t const *dict,
+ uint8_t const *data, size_t data_len, fr_aka_sim_decode_ctx_t *ctx);
+
+/*
+ * encode.c
+ */
+ssize_t fr_aka_sim_encode_pair(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx);
+
+ssize_t fr_aka_sim_encode(REQUEST *request, VALUE_PAIR *to_encode, void *encode_ctx);
+
+/*
+ * base.c
+ */
+size_t fr_aka_sim_attr_len(VALUE_PAIR const *vp);
+
+size_t fr_aka_sim_octets_prefix_len(fr_dict_attr_t const *da);
+
+int fr_aka_sim_init(void);
+
+void fr_aka_sim_free(void);
+
+/*
+ * crypto.c
+ */
+int fr_aka_sim_crypto_init_checkcode(TALLOC_CTX *ctx, fr_aka_sim_checkcode_t **checkcode,
+ EVP_MD const *md);
+
+int fr_aka_sim_crypto_update_checkcode(fr_aka_sim_checkcode_t *checkcode, eap_packet_t *eap_packet);
+
+ssize_t fr_aka_sim_crypto_finalise_checkcode(uint8_t *out, fr_aka_sim_checkcode_t **checkcode);
+
+ssize_t fr_aka_sim_crypto_sign_packet(uint8_t out[static AKA_SIM_MAC_DIGEST_SIZE],
+ eap_packet_t *eap_packet, bool zero_mac,
+ EVP_MD const *md, uint8_t const *key, size_t const key_len,
+ uint8_t const *hmac_extra, size_t const hmac_extra_len);
+
+int fr_aka_sim_crypto_gsm_kdf_0(fr_aka_sim_keys_t *keys);
+
+int fr_aka_sim_crypto_umts_kdf_0(fr_aka_sim_keys_t *keys);
+
+int fr_aka_sim_crypto_umts_kdf_1(fr_aka_sim_keys_t *keys);
+
+void fr_aka_sim_crypto_keys_init_kdf_0_reauth(fr_aka_sim_keys_t *keys,
+ uint8_t const mk[static AKA_SIM_MK_SIZE],
+ uint16_t counter);
+
+void fr_aka_sim_crypto_keys_init_umts_kdf_1_reauth(fr_aka_sim_keys_t *keys,
+ uint8_t const k_re[static AKA_SIM_K_RE_SIZE],
+ uint16_t counter);
+
+int fr_aka_sim_crypto_kdf_0_reauth(fr_aka_sim_keys_t *keys);
+
+int fr_aka_sim_crypto_umts_kdf_1_reauth(fr_aka_sim_keys_t *keys);
+
+void fr_aka_sim_crypto_keys_log(REQUEST *request, fr_aka_sim_keys_t *keys);
+
+/*
+ * vector.c
+ */
+int fr_aka_sim_vector_gsm_from_attrs(REQUEST *request, VALUE_PAIR *vps,
+ int idx,
+ fr_aka_sim_keys_t *keys,
+ fr_aka_sim_vector_src_t *src);
+
+int fr_aka_sim_vector_umts_from_attrs(REQUEST *request, VALUE_PAIR *vps,
+ fr_aka_sim_keys_t *keys,
+ fr_aka_sim_vector_src_t *src);
+
+int fr_aka_sim_vector_gsm_umts_kdf_0_reauth_from_attrs(REQUEST *request, VALUE_PAIR *vps,
+ fr_aka_sim_keys_t *keys);
+
+int fr_aka_sim_vector_umts_kdf_1_reauth_from_attrs(REQUEST *request, VALUE_PAIR *vps,
+ fr_aka_sim_keys_t *keys);
+
+void fr_aka_sim_vector_umts_reauth_clear(fr_aka_sim_keys_t *keys);
+
+int fr_aka_sim_umts_resync_from_attrs(uint64_t *new_sqn,
+ REQUEST *request, VALUE_PAIR *auts_vp,
+ fr_aka_sim_keys_t *keys);
+
+/*
+ * fips186prf.c
+ */
+void fr_aka_sim_fips186_2prf(uint8_t out[static 160], uint8_t mk[static 20]);
+
+/*
+ * xlat.c
+ */
+void fr_aka_sim_xlat_register(void);
+void fr_aka_sim_xlat_unregister(void);
*/
/**
- * @file src/lib/sim/crypto.c
+ * @file src/lib/aka-sim/crypto.c
* @brief Calculate keys from GSM vectors.
*
* The development of the original EAP/SIM support was funded by Internet Foundation
#include <stdio.h>
#include <stdlib.h>
-#include <freeradius-devel/protocol/eap/sim/dictionary.h>
+#include <freeradius-devel/protocol/eap/aka-sim/dictionary.h>
#include <freeradius-devel/eap/types.h>
-#include "base.h"
-#include "attrs.h"
-#include "milenage.h"
-
+#include <freeradius-devel/sim/common.h>
+#include <freeradius-devel/sim/milenage.h>
#include <freeradius-devel/util/sha1.h>
#include <freeradius-devel/tls/base.h>
#include <openssl/evp.h>
+#include "base.h"
+#include "attrs.h"
+
/** Free OpenSSL memory associated with our checkcode ctx
*
* @param[in] checkcode to free.
* @return 0
*/
-static int _fr_sim_crypto_free_checkcode(fr_sim_checkcode_t *checkcode)
+static int _fr_aka_sim_crypto_free_checkcode(fr_aka_sim_checkcode_t *checkcode)
{
if (checkcode->md_ctx) EVP_MD_CTX_destroy(checkcode->md_ctx);
return 0;
* - 0 on success.
* - -1 on failure.
*/
-int fr_sim_crypto_init_checkcode(TALLOC_CTX *ctx, fr_sim_checkcode_t **checkcode, EVP_MD const *md)
+int fr_aka_sim_crypto_init_checkcode(TALLOC_CTX *ctx, fr_aka_sim_checkcode_t **checkcode, EVP_MD const *md)
{
- *checkcode = talloc_zero(ctx, fr_sim_checkcode_t);
+ *checkcode = talloc_zero(ctx, fr_aka_sim_checkcode_t);
if (!*checkcode) {
fr_strerror_printf("Out of memory");
return -1;
goto error;
}
- talloc_set_destructor(*checkcode, _fr_sim_crypto_free_checkcode);
+ talloc_set_destructor(*checkcode, _fr_aka_sim_crypto_free_checkcode);
return 0;
}
/** Digest a packet, updating the checkcode
*
- * Call #fr_sim_crypto_finalise_checkcode to obtain the final checkcode value.
+ * Call #fr_aka_sim_crypto_finalise_checkcode to obtain the final checkcode value.
*
* @param[in,out] checkcode if *checkcode is NULL, a new checkcode structure
* will be allocated and the message digest context
* - 0 on success.
* - -1 on failure.
*/
-int fr_sim_crypto_update_checkcode(fr_sim_checkcode_t *checkcode, eap_packet_t *eap_packet)
+int fr_aka_sim_crypto_update_checkcode(fr_aka_sim_checkcode_t *checkcode, eap_packet_t *eap_packet)
{
uint16_t packet_len;
eap_packet_raw_t eap_hdr;
* - <= 0 on failure.
* - > 0 the number of bytes written to out.
*/
-ssize_t fr_sim_crypto_finalise_checkcode(uint8_t *out, fr_sim_checkcode_t **checkcode)
+ssize_t fr_aka_sim_crypto_finalise_checkcode(uint8_t *out, fr_aka_sim_checkcode_t **checkcode)
{
unsigned int len;
* - 0 if we found and zeroed out the mac field.
* - -1 if the field was malformed.
*/
-static int fr_sim_find_mac(uint8_t const **out, uint8_t *data, size_t data_len)
+static int fr_aka_sim_find_mac(uint8_t const **out, uint8_t *data, size_t data_len)
{
uint8_t *p = data, *end = p + data_len;
size_t len;
p += 3; /* Skip header */
while ((p + 2) < end) {
- if (p[0] == FR_SIM_MAC) {
+ if (p[0] == FR_MAC) {
len = p[1] << 2;
if ((p + len) > end) {
fr_strerror_printf("Malformed AT_MAC: Length (%zu) exceeds buffer (%zu)", len, end - p);
return -1;
}
- if (len != SIM_MAC_SIZE) {
+ if (len != AKA_SIM_MAC_SIZE) {
fr_strerror_printf("Malformed AT_MAC: Length (%zu) incorrect (%u)",
- len, SIM_MAC_SIZE);
+ len, AKA_SIM_MAC_SIZE);
return -1;
}
*out = p + 4;
* - 0 if there's no MAC attribute to verify.
* - > 0 the number of bytes written to out.
*/
-ssize_t fr_sim_crypto_sign_packet(uint8_t out[static SIM_MAC_DIGEST_SIZE],
- eap_packet_t *eap_packet, bool zero_mac,
- EVP_MD const *md, uint8_t const *key, size_t const key_len,
- uint8_t const *hmac_extra, size_t const hmac_extra_len)
+ssize_t fr_aka_sim_crypto_sign_packet(uint8_t out[static AKA_SIM_MAC_DIGEST_SIZE],
+ eap_packet_t *eap_packet, bool zero_mac,
+ EVP_MD const *md, uint8_t const *key, size_t const key_len,
+ uint8_t const *hmac_extra, size_t const hmac_extra_len)
{
EVP_MD_CTX *md_ctx = NULL;
EVP_PKEY *pkey;
* digest 16 bytes of zero.
*/
if (zero_mac) {
- switch (fr_sim_find_mac(&mac, p, end - p)) {
+ switch (fr_aka_sim_find_mac(&mac, p, end - p)) {
case 0:
{
uint8_t zero[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
MSK = FK[256..767]
EMSK = FK[768..1279]
@endverbatim
- * @note expects keys to contain a SIM_VECTOR_GSM.
+ * @note expects keys to contain a AKA_SIM_VECTOR_GSM.
*
* @param[in,out] keys Contains the authentication vectors and the buffers
* to store the result of the derivation.
* - 0 on success.
* - -1 on failure.
*/
-int fr_sim_crypto_kdf_0_gsm(fr_sim_keys_t *keys)
+int fr_aka_sim_crypto_gsm_kdf_0(fr_aka_sim_keys_t *keys)
{
fr_sha1_ctx context;
uint8_t fk[160];
- uint8_t buf[SIM_MAX_STRING_LENGTH + sizeof(keys->gsm.nonce_mt) + 2 + sizeof(keys->gsm.version_select)];
+ uint8_t buf[AKA_SIM_MAX_STRING_LENGTH + sizeof(keys->gsm.nonce_mt) + 2 + sizeof(keys->gsm.version_select)];
uint8_t *p;
- if (!fr_cond_assert(keys->vector_type == SIM_VECTOR_GSM)) return -1;
+ if (!fr_cond_assert(keys->vector_type == AKA_SIM_VECTOR_GSM)) return -1;
/*
* Our stack buffer should be large enough in
* all cases.
*/
if (!fr_cond_assert((keys->identity_len +
- (SIM_VECTOR_GSM_KC_SIZE * 3) +
+ (AKA_SIM_VECTOR_GSM_KC_SIZE * 3) +
sizeof(keys->gsm.nonce_mt) +
keys->gsm.version_list_len +
sizeof(keys->gsm.version_select)) <= sizeof(buf))) return -1;
memcpy(p, keys->identity, keys->identity_len);
p += keys->identity_len;
- memcpy(p, keys->gsm.vector[0].kc, SIM_VECTOR_GSM_KC_SIZE);
- p += SIM_VECTOR_GSM_KC_SIZE;
+ memcpy(p, keys->gsm.vector[0].kc, AKA_SIM_VECTOR_GSM_KC_SIZE);
+ p += AKA_SIM_VECTOR_GSM_KC_SIZE;
- memcpy(p, keys->gsm.vector[1].kc, SIM_VECTOR_GSM_KC_SIZE);
- p += SIM_VECTOR_GSM_KC_SIZE;
+ memcpy(p, keys->gsm.vector[1].kc, AKA_SIM_VECTOR_GSM_KC_SIZE);
+ p += AKA_SIM_VECTOR_GSM_KC_SIZE;
- memcpy(p, keys->gsm.vector[2].kc, SIM_VECTOR_GSM_KC_SIZE);
- p += SIM_VECTOR_GSM_KC_SIZE;
+ memcpy(p, keys->gsm.vector[2].kc, AKA_SIM_VECTOR_GSM_KC_SIZE);
+ p += AKA_SIM_VECTOR_GSM_KC_SIZE;
memcpy(p, keys->gsm.nonce_mt, sizeof(keys->gsm.nonce_mt));
p += sizeof(keys->gsm.nonce_mt);
*/
fr_sha1_init(&context);
fr_sha1_update(&context, buf, p - buf);
- fr_sha1_final(keys->master_key, &context);
+ fr_sha1_final(keys->mk, &context);
- FR_PROTO_HEX_DUMP(keys->master_key, sizeof(keys->master_key), "Master key");
+ FR_PROTO_HEX_DUMP(keys->mk, sizeof(keys->mk), "Master key");
/*
* Now use the PRF to expand it, generated
* k_aut, k_encr, MSK and EMSK.
*/
- fr_sim_fips186_2prf(fk, keys->master_key);
+ fr_aka_sim_fips186_2prf(fk, keys->mk);
/*
* Split up the result
p += 16;
FR_PROTO_HEX_DUMP(keys->k_encr, sizeof(keys->k_encr), "K_encr");
- memcpy(keys->k_aut, p, EAP_SIM_AUTH_SIZE); /* 128 bits for auth */
- p += EAP_SIM_AUTH_SIZE;
- keys->k_aut_len = EAP_SIM_AUTH_SIZE;
+ memcpy(keys->k_aut, p, EAP_AKA_SIM_AUTH_SIZE); /* 128 bits for auth */
+ p += EAP_AKA_SIM_AUTH_SIZE;
+ keys->k_aut_len = EAP_AKA_SIM_AUTH_SIZE;
FR_PROTO_HEX_DUMP(keys->k_aut, keys->k_aut_len, "K_aut");
memcpy(keys->msk, p, 64); /* 64 bytes for Master Session Key */
/** Key Derivation Function as described in RFC4187 (EAP-AKA) section 7
*
- * @note expects keys to contain a SIM_VECTOR_UMTS.
+ * @note expects keys to contain a AKA_SIM_VECTOR_UMTS.
*
@verbatim
MK = SHA1(Identity|IK|CK)
* - 0 on success.
* - -1 on failure.
*/
-int fr_sim_crypto_kdf_0_umts(fr_sim_keys_t *keys)
+int fr_aka_sim_crypto_umts_kdf_0(fr_aka_sim_keys_t *keys)
{
fr_sha1_ctx context;
uint8_t fk[160];
- uint8_t buf[SIM_MAX_STRING_LENGTH + sizeof(keys->umts.vector.ik) + sizeof(keys->umts.vector.ck)];
+ uint8_t buf[AKA_SIM_MAX_STRING_LENGTH + sizeof(keys->umts.vector.ik) + sizeof(keys->umts.vector.ck)];
uint8_t *p;
size_t blen;
- if (!fr_cond_assert(keys->vector_type == SIM_VECTOR_UMTS)) return - 1;
+ if (!fr_cond_assert(keys->vector_type == AKA_SIM_VECTOR_UMTS)) return - 1;
/*
* Our stack buffer should be large enough in
/* do the master key first */
fr_sha1_init(&context);
fr_sha1_update(&context, buf, blen);
- fr_sha1_final(keys->master_key, &context);
+ fr_sha1_final(keys->mk, &context);
/*
* now use the PRF to expand it, generated k_aut, k_encr,
* MSK and EMSK.
*/
- fr_sim_fips186_2prf(fk, keys->master_key);
+ fr_aka_sim_fips186_2prf(fk, keys->mk);
/* split up the result */
p = fk;
L1 = 0x00 0x06
@endverbatim
*
- * @note expects keys to contain a SIM_VECTOR_UMTS.
+ * @note expects keys to contain a AKA_SIM_VECTOR_UMTS.
*
* @param[in,out] keys Contains the authentication vectors and the buffers
* to store the result of the derivation.
* - 0 on success.
* - -1 on failure.
*/
-static int sim_crypto_derive_ck_ik_prime(fr_sim_keys_t *keys)
+static int ck_ik_prime_derive(fr_aka_sim_keys_t *keys)
{
uint8_t digest[sizeof(keys->ik_prime) + sizeof(keys->ck_prime)];
size_t len;
uint16_t l0, l1;
uint8_t k[sizeof(keys->umts.vector.ik) + sizeof(keys->umts.vector.ck)];
- uint8_t s[sizeof(uint8_t) + SIM_MAX_STRING_LENGTH + sizeof(l0) + SIM_SQN_AK_SIZE + sizeof(l1)];
+ uint8_t s[sizeof(uint8_t) + AKA_SIM_MAX_STRING_LENGTH + sizeof(l0) + AKA_SIM_SQN_AK_SIZE + sizeof(l1)];
uint8_t *p = s;
EVP_PKEY *pkey;
EVP_MD_CTX *md_ctx = NULL;
- if (!fr_cond_assert(keys->vector_type == SIM_VECTOR_UMTS)) return -1;
+ if (!fr_cond_assert(keys->vector_type == AKA_SIM_VECTOR_UMTS)) return -1;
uint48_to_buff(sqn_ak_buff, keys->sqn ^ uint48_from_buff(keys->umts.vector.ak));
if (!fr_cond_assert((sizeof(uint8_t) +
keys->network_len +
sizeof(l0) +
- SIM_SQN_AK_SIZE +
+ AKA_SIM_SQN_AK_SIZE +
sizeof(l1)) <= sizeof(s))) return -1;
FR_PROTO_HEX_DUMP(keys->network, keys->network_len, "Network");
FR_PROTO_HEX_DUMP(keys->umts.vector.ck, sizeof(keys->umts.vector.ck), "CK");
FR_PROTO_HEX_DUMP(keys->umts.vector.ik, sizeof(keys->umts.vector.ik), "IK");
- FR_PROTO_HEX_DUMP(sqn_ak_buff, SIM_SQN_AK_SIZE, "SQN ⊕ AK");
+ FR_PROTO_HEX_DUMP(sqn_ak_buff, AKA_SIM_SQN_AK_SIZE, "SQN ⊕ AK");
/*
* FC || P0 || L0 || P1 || L1 || ... || Pn || Ln
memcpy(p, &l0, sizeof(l0));
p += sizeof(l0);
- memcpy(p, sqn_ak_buff, SIM_SQN_AK_SIZE);
- p += SIM_SQN_AK_SIZE;
+ memcpy(p, sqn_ak_buff, AKA_SIM_SQN_AK_SIZE);
+ p += AKA_SIM_SQN_AK_SIZE;
- l1 = htons(SIM_SQN_AK_SIZE);
+ l1 = htons(AKA_SIM_SQN_AK_SIZE);
memcpy(p, &l1, sizeof(l1));
p += sizeof(l1);
* - 0 on success.
* - -1 on failure.
*/
-static int sim_crypto_aka_prime_prf(uint8_t *out, size_t outlen,
- uint8_t const *key, size_t key_len, uint8_t const *in, size_t in_len)
+static int aka_prime_prf(uint8_t *out, size_t outlen,
+ uint8_t const *key, size_t key_len, uint8_t const *in, size_t in_len)
{
uint8_t *p = out, *end = p + outlen;
uint8_t c = 0;
EMSK = MK[1152..1663]
@endverbatim
*
- * @note expects keys to contain a SIM_VECTOR_UMTS.
+ * @note expects keys to contain a AKA_SIM_VECTOR_UMTS.
*
* @param[in,out] keys Contains the authentication vectors and the buffers
* to store the result of the derivation.
* - 0 on success.
* - -1 on failure.
*/
-int fr_sim_crypto_kdf_1_umts(fr_sim_keys_t *keys)
+int fr_aka_sim_crypto_umts_kdf_1(fr_aka_sim_keys_t *keys)
{
uint8_t k[sizeof(keys->ck_prime) + sizeof(keys->ik_prime)];
#define KDF_1_S_STATIC "EAP-AKA'"
- uint8_t s[(sizeof(KDF_1_S_STATIC) - 1) + SIM_MAX_STRING_LENGTH];
+ uint8_t s[(sizeof(KDF_1_S_STATIC) - 1) + AKA_SIM_MAX_STRING_LENGTH];
uint8_t *p = s;
uint8_t mk[208];
size_t s_len;
- sim_crypto_derive_ck_ik_prime(keys);
+ ck_ik_prime_derive(keys);
- if (!fr_cond_assert(keys->vector_type == SIM_VECTOR_UMTS)) return -1;
+ if (!fr_cond_assert(keys->vector_type == AKA_SIM_VECTOR_UMTS)) return -1;
/*
* build s, a concatenation of EAP-AKA' and Identity
/*
* Feed into PRF
*/
- if (sim_crypto_aka_prime_prf(mk, sizeof(mk), k, sizeof(k), s, s_len) < 0) return -1;
+ if (aka_prime_prf(mk, sizeof(mk), k, sizeof(k), s, s_len) < 0) return -1;
/*
* Split the PRF output into separate keys
}
-/** Initialise fr_sim_keys_t with EAP-SIM/EAP-AKA['] reauthentication data
+/** Initialise fr_aka_sim_keys_t with EAP-SIM reauthentication data
*
- * Generates a new nonce_s and copies the mk and counter values into the fr_sim_keys_t.
+ * Generates a new nonce_s and copies the mk and counter values into the fr_aka_sim_keys_t.
*
- * @param[out] keys structure to populate.
- * @param[in] master_key from original authentication.
- * @param[in] counter re-authentication counter.
+ * @param[out] keys structure to populate.
+ * @param[in] mk from original authentication.
+ * @param[in] counter re-authentication counter.
*/
-void fr_sim_crypto_keys_init_reauth(fr_sim_keys_t *keys,
- uint8_t const master_key[static SIM_MK_SIZE], uint16_t counter)
+void fr_aka_sim_crypto_keys_init_kdf_0_reauth(fr_aka_sim_keys_t *keys,
+ uint8_t const mk[static AKA_SIM_MK_SIZE], uint16_t counter)
{
uint32_t nonce_s[4];
/*
* Copy in master key
*/
- memcpy(keys->master_key, master_key, sizeof(keys->master_key));
+ memcpy(keys->mk, mk, sizeof(keys->mk));
+
+ keys->reauth.counter = counter;
+
+ nonce_s[0] = fr_rand();
+ nonce_s[1] = fr_rand();
+ nonce_s[2] = fr_rand();
+ nonce_s[3] = fr_rand();
+ memcpy(keys->reauth.nonce_s, (uint8_t *)&nonce_s, sizeof(keys->reauth.nonce_s));
+}
+
+/** Initialise fr_aka_sim_keys_t with EAP-AKA['] reauthentication data
+ *
+ * Generates a new nonce_s and copies the mk and counter values into the fr_aka_sim_keys_t.
+ *
+ * @param[out] keys structure to populate.
+ * @param[in] k_re from original authentication.
+ * @param[in] counter re-authentication counter.
+ */
+void fr_aka_sim_crypto_keys_init_umts_kdf_1_reauth(fr_aka_sim_keys_t *keys,
+ uint8_t const k_re[static AKA_SIM_K_RE_SIZE], uint16_t counter)
+{
+ uint32_t nonce_s[4];
+
+ /*
+ * Copy in master key
+ */
+ memcpy(keys->k_re, k_re, sizeof(keys->k_re));
keys->reauth.counter = counter;
/** Key Derivation Function (Fast-Reauthentication) as described in RFC4186/7 (EAP-SIM/AKA) section 7
*
@verbatim
- XKEY' = SHA1(Identity|counter|NONCE_S| MK)
+ XKEY' = SHA1(Identity|counter|NONCE_S|MK)
FK = PRF(XKEY')
MSK = FK[0..511]
EMSK = FK[512..1023]
*
* Derives new MSK, EMSK, k_aut, k_encr
*
- * Use #fr_sim_crypto_keys_init_reauth to populate the #fr_sim_keys_t structure.
+ * Use #fr_aka_sim_crypto_keys_init_reauth to populate the #fr_aka_sim_keys_t structure.
*
- * @note expects keys to contain a populated master_key, none_s and counter values.
+ * @note expects keys to contain a populated mk, none_s and counter values.
*
* @param[in,out] keys Contains the authentication vectors and the buffers
* to store the result of the derivation.
* - 0 on success.
* - -1 on failure.
*/
-int fr_sim_crypto_kdf_0_reauth(fr_sim_keys_t *keys)
+int fr_aka_sim_crypto_kdf_0_reauth(fr_aka_sim_keys_t *keys)
{
EVP_MD_CTX *md_ctx;
uint8_t fk[160];
- uint8_t xkey_prime[SHA1_DIGEST_LENGTH];
uint8_t buf[384];
uint8_t *p;
return -1;
}
- need = keys->identity_len + sizeof(uint16_t) + SIM_NONCE_S_SIZE + sizeof(keys->master_key);
+ need = keys->identity_len + sizeof(uint16_t) + AKA_SIM_NONCE_S_SIZE + sizeof(keys->mk);
if (need > sizeof(buf)) {
fr_strerror_printf("Identity too long. PRF input is %zu bytes, input buffer is %zu bytes",
need, sizeof(buf));
* Re-derive k_aut and k_encr from the original Master Key
* These keys stay the same over multiple re-auth attempts.
*/
- fr_sim_fips186_2prf(fk, keys->master_key);
+ fr_aka_sim_fips186_2prf(fk, keys->mk);
p = fk;
memcpy(keys->k_encr, p, 16); /* 128 bits for encryption */
p += 16;
FR_PROTO_HEX_DUMP(keys->k_encr, sizeof(keys->k_encr), "K_encr");
- memcpy(keys->k_aut, p, EAP_SIM_AUTH_SIZE); /* 128 bits for auth */
+ memcpy(keys->k_aut, p, EAP_AKA_SIM_AUTH_SIZE); /* 128 bits for auth */
- keys->k_aut_len = EAP_SIM_AUTH_SIZE;
+ keys->k_aut_len = EAP_AKA_SIM_AUTH_SIZE;
FR_PROTO_HEX_DUMP(keys->k_aut, keys->k_aut_len, "K_aut");
/*
/*
* Master key
*/
- memcpy(p, keys->master_key, sizeof(keys->master_key));
- p += sizeof(keys->master_key);
+ memcpy(p, keys->mk, sizeof(keys->mk));
+ p += sizeof(keys->mk);
FR_PROTO_HEX_DUMP(buf, p - buf, "Identity || counter || NONCE_S || MK");
goto error;
}
- if (EVP_DigestFinal_ex(md_ctx, xkey_prime, &len) != 1) {
+ if (EVP_DigestFinal_ex(md_ctx, keys->reauth.xkey_prime, &len) != 1) {
tls_strerror_printf("Failed finalising digest");
goto error;
}
EVP_MD_CTX_destroy(md_ctx);
- FR_PROTO_HEX_DUMP(xkey_prime, sizeof(xkey_prime), "xkey'");
+ FR_PROTO_HEX_DUMP(keys->reauth.xkey_prime, sizeof(keys->reauth.xkey_prime), "xkey'");
/*
* Expand XKEY' with PRF
*/
- fr_sim_fips186_2prf(fk, xkey_prime);
+ fr_aka_sim_fips186_2prf(fk, keys->reauth.xkey_prime);
/*
* Split up the result
* - 0 on success.
* - -1 on failure.
*/
-int fr_sim_crypto_kdf_1_reauth(fr_sim_keys_t *keys)
+int fr_aka_sim_crypto_umts_kdf_1_reauth(fr_aka_sim_keys_t *keys)
{
#define KDF_1_S_REAUTH_STATIC "EAP-AKA' re-auth"
- uint8_t s[(sizeof(KDF_1_S_REAUTH_STATIC) - 1) + SIM_MAX_STRING_LENGTH + sizeof(uint16_t) + SIM_NONCE_S_SIZE];
+ uint8_t s[(sizeof(KDF_1_S_REAUTH_STATIC) - 1) + AKA_SIM_MAX_STRING_LENGTH + sizeof(uint16_t) + AKA_SIM_NONCE_S_SIZE];
uint8_t *p = s;
uint8_t mk[128];
if (!fr_cond_assert(((sizeof(KDF_1_S_REAUTH_STATIC) - 1) +
keys->identity_len +
sizeof(uint16_t) +
- SIM_NONCE_S_SIZE) <= sizeof(s))) return -1;
+ AKA_SIM_NONCE_S_SIZE) <= sizeof(s))) return -1;
/*
* "EAP-AKA' re-auth"
/*
* Feed into PRF
*/
- if (sim_crypto_aka_prime_prf(mk, sizeof(mk), keys->k_re, sizeof(keys->k_re), s, p - s) < 0) return -1;
+ if (aka_prime_prf(mk, sizeof(mk), keys->k_re, sizeof(keys->k_re), s, p - s) < 0) return -1;
FR_PROTO_HEX_DUMP(mk, sizeof(mk), "mk");
* @param[in] request The current request.
* @param[in] keys SIM keys associated with the session.
*/
-void fr_sim_crypto_keys_log(REQUEST *request, fr_sim_keys_t *keys)
+void fr_aka_sim_crypto_keys_log(REQUEST *request, fr_aka_sim_keys_t *keys)
{
- RDEBUG3("Cryptographic inputs");
+ RDEBUG3("KDF inputs");
RINDENT();
RHEXDUMP_INLINE3(keys->identity, keys->identity_len,
"Identity :");
switch (keys->vector_type) {
- case SIM_VECTOR_GSM:
+ case AKA_SIM_VECTOR_GSM:
{
unsigned int i;
"version_list :");
for (i = 0; i < keys->gsm.num_vectors; i++) {
- RHEXDUMP_INLINE3(keys->gsm.vector[i].rand, SIM_VECTOR_GSM_RAND_SIZE,
- "[%i] RAND :", i);
- RHEXDUMP_INLINE3(keys->gsm.vector[i].sres, SIM_VECTOR_GSM_SRES_SIZE,
- "[%i] SRES :", i);
- RHEXDUMP_INLINE3(keys->gsm.vector[i].kc, SIM_VECTOR_GSM_KC_SIZE,
- "[%i] KC :", i);
+ RHEXDUMP_INLINE3(keys->gsm.vector[i].rand, AKA_SIM_VECTOR_GSM_RAND_SIZE,
+ "[%i] RAND :", i);
+ RHEXDUMP_INLINE3(keys->gsm.vector[i].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE,
+ "[%i] SRES :", i);
+ RHEXDUMP_INLINE3(keys->gsm.vector[i].kc, AKA_SIM_VECTOR_GSM_KC_SIZE,
+ "[%i] KC :", i);
}
}
break;
- case SIM_VECTOR_UMTS:
- RHEXDUMP_INLINE3(keys->umts.vector.autn, SIM_VECTOR_UMTS_AUTN_SIZE,
- "AUTN :");
+ case AKA_SIM_VECTOR_UMTS:
+ RHEXDUMP_INLINE3(keys->umts.vector.autn, AKA_SIM_VECTOR_UMTS_AUTN_SIZE,
+ "AUTN :");
- RHEXDUMP_INLINE3(keys->umts.vector.ck, SIM_VECTOR_UMTS_CK_SIZE,
- "CK :");
+ RHEXDUMP_INLINE3(keys->umts.vector.ck, AKA_SIM_VECTOR_UMTS_CK_SIZE,
+ "CK :");
- RHEXDUMP_INLINE3(keys->umts.vector.ik, SIM_VECTOR_UMTS_IK_SIZE,
- "IK :");
+ RHEXDUMP_INLINE3(keys->umts.vector.ik, AKA_SIM_VECTOR_UMTS_IK_SIZE,
+ "IK :");
- RHEXDUMP_INLINE3(keys->umts.vector.rand, SIM_VECTOR_UMTS_RAND_SIZE,
- "RAND :");
+ RHEXDUMP_INLINE3(keys->umts.vector.rand, AKA_SIM_VECTOR_UMTS_RAND_SIZE,
+ "RAND :");
RHEXDUMP_INLINE3(keys->umts.vector.xres, keys->umts.vector.xres_len,
"XRES :");
- RHEXDUMP_INLINE3(keys->ck_prime, SIM_VECTOR_UMTS_CK_SIZE,
- "CK' :");
+ RHEXDUMP_INLINE3(keys->ck_prime, AKA_SIM_VECTOR_UMTS_CK_SIZE,
+ "CK' :");
- RHEXDUMP_INLINE3(keys->ik_prime, SIM_VECTOR_UMTS_IK_SIZE,
- "IK' :");
+ RHEXDUMP_INLINE3(keys->ik_prime, AKA_SIM_VECTOR_UMTS_IK_SIZE,
+ "IK' :");
break;
- case SIM_VECTOR_UMTS_REAUTH:
- RHEXDUMP_INLINE3(keys->master_key, sizeof(keys->master_key), "mk :");
- RDEBUG3("counter : %u", keys->reauth.counter);
- RHEXDUMP_INLINE3(keys->reauth.nonce_s, sizeof(keys->reauth.nonce_s), "nonce_s :");
+ case AKA_SIM_VECTOR_UMTS_REAUTH_KDF_0_REAUTH:
+ RHEXDUMP_INLINE3(keys->mk, sizeof(keys->mk),
+ "MK :");
+ RDEBUG3(
+ "counter : %u", keys->reauth.counter);
+ RHEXDUMP_INLINE3(keys->reauth.nonce_s, sizeof(keys->reauth.nonce_s),
+ "nonce_s :");
break;
- case SIM_VECTOR_NONE:
+ case AKA_SIM_VECTOR_UMTS_REAUTH_KDF_1_REAUTH:
+ RHEXDUMP_INLINE3(keys->k_re, sizeof(keys->k_re),
+ "k_re :");
+ RDEBUG3(
+ "counter : %u", keys->reauth.counter);
+ RHEXDUMP_INLINE3(keys->reauth.nonce_s, sizeof(keys->reauth.nonce_s),
+ "nonce_s :");
+ break;
+
+ case AKA_SIM_VECTOR_NONE:
break;
}
REXDENT();
- RDEBUG3("Derived keys");
+ RDEBUG3("Intermediary keys");
RINDENT();
-
switch (keys->vector_type) {
- case SIM_VECTOR_UMTS_REAUTH:
+ case AKA_SIM_VECTOR_UMTS_REAUTH_KDF_0_REAUTH:
+ RHEXDUMP_INLINE3(keys->reauth.xkey_prime, sizeof(keys->reauth.xkey_prime),
+ "XKEY' :");
break;
default:
- RHEXDUMP_INLINE3(keys->master_key, sizeof(keys->master_key),
- "mk :");
break;
}
+ REXDENT();
+
+ RDEBUG3("PRF output");
+ RINDENT();
+ RHEXDUMP_INLINE3(keys->mk, sizeof(keys->mk),
+ "MK :");
+ RHEXDUMP_INLINE3(keys->k_re, sizeof(keys->k_re),
+ "k_re :");
RHEXDUMP_INLINE3(keys->k_aut, keys->k_aut_len,
- "k_aut :");
+ "k_aut :");
RHEXDUMP_INLINE3(keys->k_encr, sizeof(keys->k_encr),
- "k_encr :");
- RHEXDUMP_INLINE3(keys->k_re, sizeof(keys->k_re),
- "k_re :");
+ "k_encr :");
RHEXDUMP_INLINE3(keys->msk, sizeof(keys->msk),
- "msk :");
+ "MSK :");
RHEXDUMP_INLINE3(keys->emsk, sizeof(keys->emsk),
- "emsk :");
+ "EMSK :");
REXDENT();
}
/*
* EAP-SIM (RFC4186) GSM authentication vectors
*/
-static fr_sim_keys_t const rfc4186_vector0_in = {
+static fr_aka_sim_keys_t const rfc4186_vector0_in = {
.identity = (uint8_t const *)"1244070100000001@eapsim.foo",
.identity_len = sizeof("1244070100000001@eapsim.foo") - 1,
.version_select = { 0x00, 0x01 },
.num_vectors = 3
},
- .vector_type = SIM_VECTOR_GSM
+ .vector_type = AKA_SIM_VECTOR_GSM
};
-static fr_sim_keys_t const rfc4186_vector0_out = {
+static fr_aka_sim_keys_t const rfc4186_vector0_out = {
.k_encr = { 0x53, 0x6e, 0x5e, 0xbc, 0x44, 0x65, 0x58, 0x2a,
0xa6, 0xa8, 0xec, 0x99, 0x86, 0xeb, 0xb6, 0x20 },
.k_aut = { 0x25, 0xaf, 0x19, 0x42, 0xef, 0xcb, 0xf4, 0xbc,
static void test_eap_sim_kdf_0_gsm(void)
{
- fr_sim_keys_t keys;
+ fr_aka_sim_keys_t keys;
int ret;
/*
memcpy(&keys, &rfc4186_vector0_in, sizeof(keys));
- ret = fr_sim_crypto_kdf_0_gsm(&keys);
+ ret = fr_aka_sim_crypto_gsm_kdf_0(&keys);
TEST_CHECK(ret == 0);
TEST_CHECK(memcmp(&rfc4186_vector0_out.k_encr, keys.k_encr, sizeof(keys.k_encr)) == 0);
*
* Test vector from 18 from 3GPP TS 35.208 V9 (the same used by EAP-AKA')
*/
-static fr_sim_keys_t const rfc4187_vector0_in = {
+static fr_aka_sim_keys_t const rfc4187_vector0_in = {
.identity = (uint8_t const *)"0555444333222111",
.identity_len = sizeof("0555444333222111") - 1,
.xres_len = 8
}
},
- .vector_type = SIM_VECTOR_UMTS
+ .vector_type = AKA_SIM_VECTOR_UMTS
};
-static fr_sim_keys_t const rfc4187_vector0_out = {
+static fr_aka_sim_keys_t const rfc4187_vector0_out = {
.k_encr = { 0x18, 0xe8, 0xb2, 0x0b, 0xcd, 0xa7, 0x04, 0x86,
0xfd, 0x59, 0x59, 0x58, 0x6a, 0x9e, 0x7c, 0x3d },
.k_aut = { 0x18, 0xc0, 0x44, 0x07, 0x0e, 0x5e, 0x64, 0x2a,
static void test_eap_aka_kdf_0_umts(void)
{
- fr_sim_keys_t keys;
+ fr_aka_sim_keys_t keys;
int ret;
/*
memcpy(&keys, &rfc4187_vector0_in, sizeof(keys));
- ret = fr_sim_crypto_kdf_0_umts(&keys);
+ ret = fr_aka_sim_crypto_umts_kdf_0(&keys);
TEST_CHECK(ret == 0);
TEST_CHECK(memcmp(&rfc4187_vector0_out.k_encr, keys.k_encr, sizeof(keys.k_encr)) == 0);
/*
* EAP-SIM (RFC4186) GSM re-authentication vectors
*/
-static fr_sim_keys_t const rfc4186_vector0_reauth_in = {
+static fr_aka_sim_keys_t const rfc4186_vector0_reauth_in = {
.identity = (uint8_t const *)"Y24fNSrz8BP274jOJaF17WfxI8YO7QX00pMXk9XMMVOw7broaNhTczuFq53aEpOkk3L0dm@eapsim.foo",
.identity_len = sizeof("Y24fNSrz8BP274jOJaF17WfxI8YO7QX00pMXk9XMMVOw7broaNhTczuFq53aEpOkk3L0dm@eapsim.foo") - 1,
.nonce_s = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }
},
- .master_key = { 0xe5, 0x76, 0xd5, 0xca, 0x33, 0x2e, 0x99, 0x30,
+ .mk = { 0xe5, 0x76, 0xd5, 0xca, 0x33, 0x2e, 0x99, 0x30,
0x01, 0x8b, 0xf1, 0xba, 0xee, 0x27, 0x63, 0xc7,
0x95, 0xb3, 0xc7, 0x12 },
};
-static fr_sim_keys_t const rfc4186_vector0_reauth_out = {
+static fr_aka_sim_keys_t const rfc4186_vector0_reauth_out = {
.k_encr = { 0x53, 0x6e, 0x5e, 0xbc, 0x44, 0x65, 0x58, 0x2a,
0xa6, 0xa8, 0xec, 0x99, 0x86, 0xeb, 0xb6, 0x20 },
.k_aut = { 0x25, 0xaf, 0x19, 0x42, 0xef, 0xcb, 0xf4, 0xbc,
static void test_eap_sim_kdf_0_reauth(void)
{
- fr_sim_keys_t keys;
+ fr_aka_sim_keys_t keys;
int ret;
/*
memcpy(&keys, &rfc4186_vector0_reauth_in, sizeof(keys));
- ret = fr_sim_crypto_kdf_0_reauth(&keys);
+ ret = fr_aka_sim_crypto_kdf_0_reauth(&keys);
TEST_CHECK(ret == 0);
TEST_CHECK(memcmp(&rfc4186_vector0_reauth_out.k_encr, keys.k_encr, sizeof(keys.k_encr)) == 0);
/*
* EAP-AKA' (RFC5448) UMTS authentication vectors
*/
-static fr_sim_keys_t const rfc5448_vector0_in = {
+static fr_aka_sim_keys_t const rfc5448_vector0_in = {
.identity = (uint8_t const *)"0555444333222111",
.identity_len = sizeof("0555444333222111") - 1,
.xres_len = 8
}
},
- .vector_type = SIM_VECTOR_UMTS
+ .vector_type = AKA_SIM_VECTOR_UMTS
};
-static fr_sim_keys_t const rfc5448_vector0_out = {
+static fr_aka_sim_keys_t const rfc5448_vector0_out = {
.ik_prime = { 0x00, 0x93, 0x96, 0x2d, 0x0d, 0xd8, 0x4a, 0xa5,
0x68, 0x4b, 0x04, 0x5c, 0x9e, 0xdf, 0xfa, 0x04 },
.ck_prime = { 0xcc, 0xfc, 0x23, 0x0c, 0xa7, 0x4f, 0xcc, 0x96,
static void test_eap_aka_kdf_1_umts(void)
{
- fr_sim_keys_t keys;
+ fr_aka_sim_keys_t keys;
int ret;
/*
memcpy(keys.ck_prime, rfc5448_vector0_out.ck_prime, sizeof(keys.ck_prime));
memcpy(keys.ik_prime, rfc5448_vector0_out.ik_prime, sizeof(keys.ik_prime));
- ret = fr_sim_crypto_kdf_1_umts(&keys);
+ ret = fr_aka_sim_crypto_umts_kdf_1(&keys);
TEST_CHECK(ret == 0);
TEST_CHECK(memcmp(&rfc5448_vector0_out.k_encr, keys.k_encr, sizeof(keys.k_encr)) == 0);
static void test_eap_aka_derive_ck_ik(void)
{
- fr_sim_keys_t keys;
+ fr_aka_sim_keys_t keys;
int ret;
/*
*/
memcpy(&keys, &rfc5448_vector0_in, sizeof(keys));
- ret = sim_crypto_derive_ck_ik_prime(&keys);
+ ret = ck_ik_prime_derive(&keys);
TEST_CHECK(ret == 0);
TEST_CHECK(memcmp(&rfc5448_vector0_out.ck_prime, keys.ck_prime, sizeof(keys.ck_prime)) == 0);
TEST_CHECK(memcmp(&rfc5448_vector0_out.ik_prime, keys.ik_prime, sizeof(keys.ik_prime)) == 0);
/*
* EAP-AKA' (RFC5448) UMTS authentication vectors
*/
-static fr_sim_keys_t const rfc5448_vector0_reauth_in = {
+static fr_aka_sim_keys_t const rfc5448_vector0_reauth_in = {
.identity = (uint8_t const *)"5555444333222111",
.identity_len = sizeof("5555444333222111") - 1,
/*
* Not tested against external source (yet)
*/
-static fr_sim_keys_t const rfc5448_vector0_reauth_out = {
+static fr_aka_sim_keys_t const rfc5448_vector0_reauth_out = {
.k_encr = { 0x76, 0x6f, 0xa0, 0xa6, 0xc3, 0x17, 0x17, 0x4b,
0x81, 0x2d, 0x52, 0xfb, 0xcd, 0x11, 0xa1, 0x79 },
.k_aut = { 0x08, 0x42, 0xea, 0x72, 0x2f, 0xf6, 0x83, 0x5b,
static void test_eap_aka_kdf_1_reauth(void)
{
- fr_sim_keys_t keys;
+ fr_aka_sim_keys_t keys;
int ret;
/*
memcpy(&keys, &rfc5448_vector0_reauth_in, sizeof(keys));
- ret = fr_sim_crypto_kdf_1_reauth(&keys);
+ ret = fr_aka_sim_crypto_umts_kdf_1_reauth(&keys);
TEST_CHECK(ret == 0);
TEST_CHECK(memcmp(&rfc5448_vector0_reauth_out.k_encr, keys.k_encr, sizeof(keys.k_encr)) == 0);
*/
/**
- * @file src/lib/sim/decode.c
+ * @file src/lib/aka-sim/decode.c
* @brief Code common to EAP-SIM/AKA/AKA' clients and servers.
*
* The development of the EAP-SIM support was funded by Internet Foundation
#include <freeradius-devel/io/test_point.h>
#include <freeradius-devel/eap/types.h>
-#include "eap_sim_common.h"
#include "base.h"
#include "attrs.h"
* - 0 on success.
* - < 0 on failure (bad IV).
*/
-static inline int sim_iv_extract(uint8_t out[SIM_IV_SIZE], uint8_t const *in, size_t in_len)
+static inline int sim_iv_extract(uint8_t out[AKA_SIM_IV_SIZE], uint8_t const *in, size_t in_len)
{
/*
* Two bytes are reserved, so although
* the IV is actually 16 bytes, we
* check for 18.
*/
- if (in_len != (SIM_IV_SIZE + 2)) {
+ if (in_len != (AKA_SIM_IV_SIZE + 2)) {
fr_strerror_printf("%s: Invalid IV length, expected %u got %zu",
- __FUNCTION__, (SIM_IV_SIZE + 2), in_len);
+ __FUNCTION__, (AKA_SIM_IV_SIZE + 2), in_len);
return -1;
}
}
/* skip reserved bytes */
- memcpy(out, in + 2, SIM_IV_SIZE);
+ memcpy(out, in + 2, AKA_SIM_IV_SIZE);
return 0;
}
uint8_t const *data, size_t const attr_len, size_t const data_len,
void *decoder_ctx)
{
- fr_sim_decode_ctx_t *packet_ctx = decoder_ctx;
+ fr_aka_sim_decode_ctx_t *packet_ctx = decoder_ctx;
EVP_CIPHER_CTX *evp_ctx;
EVP_CIPHER const *evp_cipher = EVP_aes_128_cbc();
size_t block_size = EVP_CIPHER_block_size(evp_cipher);
return -1;
}
- if (sim_at == FR_SIM_IV) {
+ if (sim_at == FR_IV) {
if (sim_iv_extract(&(packet_ctx->iv[0]), p + 2, sim_at_len - 2) < 0) return -1;
packet_ctx->have_iv = true;
break;
break;
default:
- element_len = fr_sim_attr_sizes[da->type][0];
+ element_len = fr_aka_sim_attr_sizes[da->type][0];
break;
}
* correct length for the block cipher
* (16 in the case of AES-128-CBC).
*/
- if (sim_at == FR_SIM_PADDING) {
+ if (sim_at == FR_PADDING) {
uint8_t zero = 0;
uint8_t i;
}
/*
- * RFC says we MUST verify that FR_SIM_PADDING
+ * RFC says we MUST verify that FR_PADDING
* data is zeroed out.
*/
for (i = 2; i < sim_at_len; i++) zero |= p[i];
* RFC says we need to die on these if we don't
* understand them. non-skippables are < 128.
*/
- if (sim_at <= SIM_SKIPPABLE_MAX) {
+ if (sim_at <= AKA_SIM_SKIPPABLE_MAX) {
fr_strerror_printf("%s: Unknown (non-skippable) attribute %i",
__FUNCTION__, sim_at);
goto error;
uint8_t const *p = data;
size_t prefix = 0;
- fr_sim_decode_ctx_t *packet_ctx = decoder_ctx;
+ fr_aka_sim_decode_ctx_t *packet_ctx = decoder_ctx;
if (!fr_cond_assert(attr_len <= data_len)) return -1;
if (!fr_cond_assert(parent)) return -1;
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- case FR_SIM_IV:
+ case FR_IV:
if (sim_iv_extract(&packet_ctx->iv[0], data, attr_len) < 0) return -1;
packet_ctx->have_iv = true;
break; /* Now create the attribute */
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- case FR_EAP_AKA_RES:
+ case FR_RES:
{
uint16_t res_len;
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- case FR_EAP_AKA_CHECKCODE:
+ case FR_CHECKCODE:
if (attr_len < 2) goto raw; /* Need at least two bytes for reserved field */
vp = fr_pair_afrom_da(ctx, parent);
/*
* Get the number of bytes we expect before the value
*/
- prefix = fr_sim_octets_prefix_len(parent);
+ prefix = fr_aka_sim_octets_prefix_len(parent);
if (attr_len < prefix) goto raw;
if (parent->flags.length && (attr_len != (parent->flags.length + prefix))) goto wrong_len;
break;
case FR_TYPE_UINT16:
case FR_TYPE_UINT32:
case FR_TYPE_UINT64:
- if (attr_len != fr_sim_attr_sizes[parent->type][0]) goto raw;
+ if (attr_len != fr_aka_sim_attr_sizes[parent->type][0]) goto raw;
break;
case FR_TYPE_TLV:
* We can't create unknowns for non-skippable attributes
* as we're prohibited from continuing by the SIM RFCs.
*/
- if (parent->attr <= SIM_SKIPPABLE_MAX) {
+ if (parent->attr <= AKA_SIM_SKIPPABLE_MAX) {
fr_strerror_printf_push("%s: Failed parsing non-skippable attribute '%s'",
__FUNCTION__, parent->name);
return -1;
* RFC says we need to die on these if we don't
* understand them. non-skippables are < 128.
*/
- if (sim_at <= SIM_SKIPPABLE_MAX) {
+ if (sim_at <= AKA_SIM_SKIPPABLE_MAX) {
fr_strerror_printf("Unknown (non-skippable) attribute %i", sim_at);
return -1;
}
* - The number of bytes parsed.
* - -1 on error.
*/
-ssize_t fr_sim_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_t const *dict,
+ssize_t fr_aka_sim_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_t const *dict,
uint8_t const *data, size_t data_len, void *decoder_ctx)
{
return sim_decode_pair_internal(ctx, cursor, fr_dict_root(dict), data, data_len, decoder_ctx);
* - 0 on success.
* - -1 on failure.
*/
-int fr_sim_decode(REQUEST *request, fr_cursor_t *decoded, fr_dict_t const *dict,
- uint8_t const *data, size_t data_len, fr_sim_decode_ctx_t *decoder_ctx)
+int fr_aka_sim_decode(REQUEST *request, fr_cursor_t *decoded, fr_dict_t const *dict,
+ uint8_t const *data, size_t data_len, fr_aka_sim_decode_ctx_t *decoder_ctx)
{
ssize_t rcode;
uint8_t const *p = data;
* in the SIM/AKA/AKA' dict.
*/
while (p < end) {
- rcode = fr_sim_decode_pair(request->packet, decoded, dict, p, end - p, decoder_ctx);
+ rcode = fr_aka_sim_decode_pair(request->packet, decoded, dict, p, end - p, decoder_ctx);
if (rcode <= 0) {
RPEDEBUG("Failed decoding AT");
error:
{
VALUE_PAIR *vp;
- vp = fr_pair_afrom_child_num(request->packet, fr_dict_root(dict), FR_SIM_SUBTYPE);
+ vp = fr_pair_afrom_child_num(request->packet, fr_dict_root(dict), FR_SUBTYPE);
if (!vp) {
fr_strerror_printf("Failed allocating subtype attribute");
goto error;
return 0;
}
-static int _test_ctx_free(UNUSED fr_sim_decode_ctx_t *ctx)
+static int _test_ctx_free(UNUSED fr_aka_sim_decode_ctx_t *ctx)
{
- fr_sim_free();
+ fr_aka_sim_free();
return 0;
}
-static fr_sim_decode_ctx_t *test_ctx_init(TALLOC_CTX *ctx, uint8_t const *k_encr, size_t k_encr_len)
+static fr_aka_sim_decode_ctx_t *test_ctx_init(TALLOC_CTX *ctx, uint8_t const *k_encr, size_t k_encr_len)
{
- fr_sim_decode_ctx_t *test_ctx;
- fr_sim_keys_t *keys;
+ fr_aka_sim_decode_ctx_t *test_ctx;
+ fr_aka_sim_keys_t *keys;
- if (fr_sim_init() < 0) return NULL;
+ if (fr_aka_sim_init() < 0) return NULL;
- test_ctx = talloc_zero(ctx, fr_sim_decode_ctx_t);
- test_ctx->keys = keys = talloc_zero(test_ctx, fr_sim_keys_t);
+ test_ctx = talloc_zero(ctx, fr_aka_sim_decode_ctx_t);
+ test_ctx->keys = keys = talloc_zero(test_ctx, fr_aka_sim_keys_t);
memcpy(keys->k_encr, k_encr, k_encr_len);
talloc_set_destructor(test_ctx, _test_ctx_free);
*/
static int decode_test_ctx_sim(void **out, TALLOC_CTX *ctx)
{
- fr_sim_decode_ctx_t *test_ctx;
+ fr_aka_sim_decode_ctx_t *test_ctx;
static uint8_t k_encr[] = { 0x00, 0x01, 0x02, 0x03, 0x04 ,0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
static int decode_test_ctx_aka(void **out, TALLOC_CTX *ctx)
{
- fr_sim_decode_ctx_t *test_ctx;
+ fr_aka_sim_decode_ctx_t *test_ctx;
static uint8_t k_encr[] = { 0x00, 0x01, 0x02, 0x03, 0x04 ,0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
static int decode_test_ctx_sim_rfc4186(void **out, TALLOC_CTX *ctx)
{
- fr_sim_decode_ctx_t *test_ctx;
+ fr_aka_sim_decode_ctx_t *test_ctx;
static uint8_t k_encr[] = { 0x53, 0x6e, 0x5e, 0xbc, 0x44 ,0x65, 0x58, 0x2a,
0xa6, 0xa8, 0xec, 0x99, 0x86, 0xeb, 0xb6, 0x20 };
extern fr_test_point_pair_decode_t sim_tp_decode;
fr_test_point_pair_decode_t sim_tp_decode = {
.test_ctx = decode_test_ctx_sim,
- .func = fr_sim_decode_pair
+ .func = fr_aka_sim_decode_pair
};
extern fr_test_point_pair_decode_t sim_tp_decode_rfc4186;
fr_test_point_pair_decode_t sim_tp_decode_rfc4186 = {
.test_ctx = decode_test_ctx_sim_rfc4186,
- .func = fr_sim_decode_pair
+ .func = fr_aka_sim_decode_pair
};
extern fr_test_point_pair_decode_t aka_tp_decode;
fr_test_point_pair_decode_t aka_tp_decode = {
.test_ctx = decode_test_ctx_aka,
- .func = fr_sim_decode_pair
+ .func = fr_aka_sim_decode_pair
};
*/
/**
- * @file src/lib/sim/encode.c
+ * @file src/lib/aka-sim/encode.c
* @brief Code common to EAP-SIM/AKA/AKA' clients and servers.
*
* @copyright 2017 FreeRADIUS server project
#include <freeradius-devel/io/test_point.h>
#include <freeradius-devel/eap/types.h>
-#include "eap_sim_common.h"
#include "base.h"
#include "attrs.h"
static inline VALUE_PAIR *next_encodable(fr_cursor_t *cursor, void *encoder_ctx)
{
VALUE_PAIR *vp;
- fr_sim_encode_ctx_t *packet_ctx = encoder_ctx;
+ fr_aka_sim_encode_ctx_t *packet_ctx = encoder_ctx;
while ((vp = fr_cursor_next(cursor))) if (is_encodable(packet_ctx->root, vp)) break;
return fr_cursor_current(cursor);
static inline VALUE_PAIR *first_encodable(fr_cursor_t *cursor, void *encoder_ctx)
{
VALUE_PAIR *vp;
- fr_sim_encode_ctx_t *packet_ctx = encoder_ctx;
+ fr_aka_sim_encode_ctx_t *packet_ctx = encoder_ctx;
vp = fr_cursor_current(cursor);
if (is_encodable(packet_ctx->root, vp)) return vp;
*/
static ssize_t encode_iv(uint8_t *out, size_t outlen, void *encoder_ctx)
{
- fr_sim_encode_ctx_t *packet_ctx = encoder_ctx;
+ fr_aka_sim_encode_ctx_t *packet_ctx = encoder_ctx;
uint8_t *p = out;
uint32_t iv[4];
*/
if (packet_ctx->iv_included) return 0;
- CHECK_FREESPACE(outlen, 4 + SIM_IV_SIZE); /* AT_IV + Length + Reserved(2) + IV */
+ CHECK_FREESPACE(outlen, 4 + AKA_SIM_IV_SIZE); /* AT_IV + Length + Reserved(2) + IV */
/*
* Generate IV
memcpy(packet_ctx->iv, (uint8_t *)&iv[0], sizeof(packet_ctx->iv)); /* ensures alignment */
- *p++ = FR_SIM_IV;
- *p++ = (4 + SIM_IV_SIZE) >> 2;
+ *p++ = FR_IV;
+ *p++ = (4 + AKA_SIM_IV_SIZE) >> 2;
*p++ = 0;
*p++ = 0;
memcpy(p, packet_ctx->iv, sizeof(packet_ctx->iv));
{
size_t total_len, pad_len, encr_len, len = 0;
uint8_t *p = out, *encr = NULL;
- fr_sim_encode_ctx_t *packet_ctx = encoder_ctx;
+ fr_aka_sim_encode_ctx_t *packet_ctx = encoder_ctx;
EVP_CIPHER_CTX *evp_ctx;
EVP_CIPHER const *evp_cipher = EVP_aes_128_cbc();
size_t block_size = EVP_CIPHER_block_size(evp_cipher);
* Append an AT_PADDING attribute if required
*/
if (pad_len != 0) {
- p[0] = FR_SIM_PADDING;
+ p[0] = FR_PADDING;
p[1] = pad_len >> 2;
memset(p + 2, 0, pad_len - 2); /* Ensure the rest is zeroed out */
FR_PROTO_HEX_DUMP(p, pad_len, "Done padding attribute");
ssize_t len;
VALUE_PAIR const *vp = fr_cursor_current(cursor);
fr_dict_attr_t const *da = tlv_stack[depth];
- fr_sim_encode_ctx_t *packet_ctx = encoder_ctx;
+ fr_aka_sim_encode_ctx_t *packet_ctx = encoder_ctx;
VP_VERIFY(vp);
FR_PROTO_STACK_PRINT(tlv_stack, depth);
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- case FR_SIM_IV:
+ case FR_IV:
if ((vp->da->flags.length && (da->flags.length != vp->vp_length)) ||
(vp->vp_length != sizeof(packet_ctx->iv))) {
fr_strerror_printf("%s: Attribute \"%s\" needs a value of exactly %zu bytes, "
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- case FR_EAP_AKA_RES:
+ case FR_RES:
{
uint16_t res_len = htons(vp->vp_length * 8); /* Get length in bits */
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- case FR_EAP_AKA_CHECKCODE:
+ case FR_CHECKCODE:
{
uint8_t *p = out;
* Fixed length attribute
*/
if (vp->da->flags.length) {
- size_t prefix = fr_sim_octets_prefix_len(vp->da);
+ size_t prefix = fr_aka_sim_octets_prefix_len(vp->da);
size_t pad_len;
size_t value_len_rounded;
}
element_len = da->flags.length;
} else {
- element_len = fr_sim_attr_sizes[da->type][0];
+ element_len = fr_aka_sim_attr_sizes[da->type][0];
}
/*
return p - out; /* AT_IV + AT_*(TLV) - Can't use total_len, doesn't include IV */
}
-ssize_t fr_sim_encode_pair(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx)
+ssize_t fr_aka_sim_encode_pair(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx)
{
VALUE_PAIR const *vp;
ssize_t slen;
fr_dict_attr_t const *tlv_stack[FR_DICT_MAX_TLV_STACK + 1];
fr_dict_attr_t const *da = NULL;
- fr_sim_encode_ctx_t *packet_ctx = encoder_ctx;
+ fr_aka_sim_encode_ctx_t *packet_ctx = encoder_ctx;
if (!cursor || !out) return PAIR_ENCODE_ERROR;
return PAIR_ENCODE_ERROR;
}
- if (vp->da->attr == FR_SIM_MAC) {
+ if (vp->da->attr == FR_MAC) {
next_encodable(cursor, encoder_ctx);
return 0;
}
return slen;
}
-ssize_t fr_sim_encode(REQUEST *request, VALUE_PAIR *to_encode, void *encode_ctx)
+ssize_t fr_aka_sim_encode(REQUEST *request, VALUE_PAIR *to_encode, void *encode_ctx)
{
VALUE_PAIR *vp;
unsigned char subtype;
fr_cursor_t cursor;
- fr_sim_encode_ctx_t *packet_ctx = encode_ctx;
+ fr_aka_sim_encode_ctx_t *packet_ctx = encode_ctx;
eap_packet_t *eap_packet = packet_ctx->eap_packet;
/*
* It might be too big for putting into an
* EAP packet.
*/
- vp = fr_pair_find_by_child_num(to_encode, packet_ctx->root, FR_SIM_SUBTYPE, TAG_ANY);
+ vp = fr_pair_find_by_child_num(to_encode, packet_ctx->root, FR_SUBTYPE, TAG_ANY);
if (!vp) {
REDEBUG("Missing subtype attribute");
return PAIR_ENCODE_ERROR;
/*
* Will we need to generate a HMAC?
*/
- if (fr_pair_find_by_child_num(to_encode, packet_ctx->root, FR_EAP_SIM_MAC, TAG_ANY)) do_hmac = true;
+ if (fr_pair_find_by_child_num(to_encode, packet_ctx->root, FR_MAC, TAG_ANY)) do_hmac = true;
/*
* Fast path, we just need to encode a subtype
* Add space in the packet for AT_MAC
*/
if (do_hmac) {
- CHECK_FREESPACE(end - p, SIM_MAC_SIZE);
+ CHECK_FREESPACE(end - p, AKA_SIM_MAC_SIZE);
- *p++ = FR_SIM_MAC;
- *p++ = (SIM_MAC_SIZE >> 2);
+ *p++ = FR_MAC;
+ *p++ = (AKA_SIM_MAC_SIZE >> 2);
*p++ = 0x00;
*p++ = 0x00;
hmac = p;
*/
(void)fr_cursor_head(&cursor);
while ((vp = fr_cursor_current(&cursor))) {
- slen = fr_sim_encode_pair(p, end - p, &cursor, packet_ctx);
+ slen = fr_aka_sim_encode_pair(p, end - p, &cursor, packet_ctx);
if (slen < 0) {
error:
talloc_free(buff);
* Calculate a SHA1-HMAC over the complete EAP packet
*/
if (do_hmac) {
- slen = fr_sim_crypto_sign_packet(hmac, eap_packet, false,
+ slen = fr_aka_sim_crypto_sign_packet(hmac, eap_packet, false,
packet_ctx->hmac_md,
packet_ctx->keys->k_aut, packet_ctx->keys->k_aut_len,
packet_ctx->hmac_extra, packet_ctx->hmac_extra_len);
if (slen < 0) goto error;
- FR_PROTO_HEX_DUMP(hmac - 4, SIM_MAC_SIZE, "hmac attribute");
+ FR_PROTO_HEX_DUMP(hmac - 4, AKA_SIM_MAC_SIZE, "hmac attribute");
}
FR_PROTO_HEX_DUMP(buff, eap_packet->type.length, "sim packet");
/*
* Test ctx data
*/
-static int _test_ctx_free(UNUSED fr_sim_encode_ctx_t *ctx)
+static int _test_ctx_free(UNUSED fr_aka_sim_encode_ctx_t *ctx)
{
- fr_sim_free();
+ fr_aka_sim_free();
return 0;
}
-static fr_sim_encode_ctx_t *test_ctx_init(TALLOC_CTX *ctx, uint8_t const *k_encr, size_t k_encr_len)
+static fr_aka_sim_encode_ctx_t *test_ctx_init(TALLOC_CTX *ctx, uint8_t const *k_encr, size_t k_encr_len)
{
- fr_sim_encode_ctx_t *test_ctx;
- fr_sim_keys_t *keys;
+ fr_aka_sim_encode_ctx_t *test_ctx;
+ fr_aka_sim_keys_t *keys;
- test_ctx = talloc_zero(ctx, fr_sim_encode_ctx_t);
- test_ctx->keys = keys = talloc_zero(test_ctx, fr_sim_keys_t);
+ test_ctx = talloc_zero(ctx, fr_aka_sim_encode_ctx_t);
+ test_ctx->keys = keys = talloc_zero(test_ctx, fr_aka_sim_keys_t);
memcpy(keys->k_encr, k_encr, k_encr_len);
talloc_set_destructor(test_ctx, _test_ctx_free);
- if (fr_sim_init() < 0) return NULL;
+ if (fr_aka_sim_init() < 0) return NULL;
return test_ctx;
}
*/
static int encode_test_ctx_sim(void **out, TALLOC_CTX *ctx)
{
- fr_sim_encode_ctx_t *test_ctx;
+ fr_aka_sim_encode_ctx_t *test_ctx;
static uint8_t k_encr[] = { 0x00, 0x01, 0x02, 0x03, 0x04 ,0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
test_ctx = test_ctx_init(ctx, k_encr, sizeof(k_encr));
if (!test_ctx) return -1;
- test_ctx->root = fr_dict_root(dict_eap_sim);
+ test_ctx->root = fr_dict_root(dict_eap_aka_sim);
test_ctx->iv_included = true; /* Ensures IV is all zeros */
*out = test_ctx;
static int encode_test_ctx_aka(void **out, TALLOC_CTX *ctx)
{
- fr_sim_encode_ctx_t *test_ctx;
+ fr_aka_sim_encode_ctx_t *test_ctx;
static uint8_t k_encr[] = { 0x00, 0x01, 0x02, 0x03, 0x04 ,0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
test_ctx = test_ctx_init(ctx, k_encr, sizeof(k_encr));
if (!test_ctx) return -1;
- test_ctx->root = fr_dict_root(dict_eap_aka);
+ test_ctx->root = fr_dict_root(dict_eap_aka_sim);
test_ctx->iv_included = true; /* Ensures IV is all zeros */
*out = test_ctx;
static int encode_test_ctx_sim_rfc4186(void **out, TALLOC_CTX *ctx)
{
- fr_sim_encode_ctx_t *test_ctx;
+ fr_aka_sim_encode_ctx_t *test_ctx;
static uint8_t k_encr[] = { 0x53, 0x6e, 0x5e, 0xbc, 0x44 ,0x65, 0x58, 0x2a,
0xa6, 0xa8, 0xec, 0x99, 0x86, 0xeb, 0xb6, 0x20 };
test_ctx = test_ctx_init(ctx, k_encr, sizeof(k_encr));
if (!test_ctx) return -1;
- test_ctx->root = fr_dict_root(dict_eap_sim);
+ test_ctx->root = fr_dict_root(dict_eap_aka_sim);
*out = test_ctx;
extern fr_test_point_pair_encode_t sim_tp_encode;
fr_test_point_pair_encode_t sim_tp_encode = {
.test_ctx = encode_test_ctx_sim,
- .func = fr_sim_encode_pair
+ .func = fr_aka_sim_encode_pair
};
extern fr_test_point_pair_encode_t aka_tp_encode;
fr_test_point_pair_encode_t aka_tp_encode = {
.test_ctx = encode_test_ctx_aka,
- .func = fr_sim_encode_pair
+ .func = fr_aka_sim_encode_pair
};
extern fr_test_point_pair_encode_t sim_tp_encode_rfc4186;
fr_test_point_pair_encode_t sim_tp_encode_rfc4186 = {
.test_ctx = encode_test_ctx_sim_rfc4186,
- .func = fr_sim_encode_pair
+ .func = fr_aka_sim_encode_pair
};
/**
* $Id$
- * @file src/lib/sim/fips186prf.c
+ * @file src/lib/aka-sim/fips186prf.c
* @brief EAP sim protocol encoders and decoders.
*
* The development of the EAP/SIM support was funded by Internet Foundation
* This code was written from scratch by Michael Richardson, and is
* dual licensed under both GPL and BSD.
*
- * @copyright 2003 Michael Richardson (mcr@sandelman.ottawa.on.ca)
+ * @copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
* @copyright 2003-2016 The FreeRADIUS server project
*/
RCSID("$Id$")
* @param[in] mk The master key we use to derive all other keying
* data.
*/
-void fr_sim_fips186_2prf(uint8_t out[static 160], uint8_t mk[static 20])
+void fr_aka_sim_fips186_2prf(uint8_t out[static 160], uint8_t mk[static 20])
{
fr_sha1_ctx context;
int j;
*/
/**
- * @file src/lib/sim/id.c
+ * @file src/lib/aka-sim/id.c
* @brief EAP-SIM/EAP-AKA identity detection, creation, and decyption.
*
* @copyright 2017 The FreeRADIUS server project
#define us(x) (uint8_t) x
-fr_table_num_sorted_t const sim_id_request_table[] = {
- { "Any-Id-Req", SIM_ANY_ID_REQ },
- { "FullAuth-Id-Req", SIM_FULLAUTH_ID_REQ },
- { "Permanent-Id-Req", SIM_PERMANENT_ID_REQ }
+fr_table_num_sorted_t const fr_aka_sim_id_request_table[] = {
+ { "Any-Id-Req", AKA_SIM_ANY_ID_REQ },
+ { "FullAuth-Id-Req", AKA_SIM_FULLAUTH_ID_REQ },
+ { "no", AKA_SIM_NO_ID_REQ }, /* Used for config parsing */
+ { "none", AKA_SIM_NO_ID_REQ },
+ { "Permanent-Id-Req", AKA_SIM_PERMANENT_ID_REQ },
+ { NULL }
};
-size_t sim_id_request_table_len = NUM_ELEMENTS(sim_id_request_table);
+size_t fr_aka_sim_id_request_table_len = NUM_ELEMENTS(fr_aka_sim_id_request_table);
-fr_table_num_sorted_t const sim_id_method_hint_table[] = {
- { "AKA", SIM_METHOD_HINT_AKA },
- { "AKA'", SIM_METHOD_HINT_AKA_PRIME },
- { "SIM", SIM_METHOD_HINT_SIM }
+fr_table_num_sorted_t const fr_aka_sim_id_method_table[] = {
+ { "AKA'", AKA_SIM_METHOD_HINT_AKA_PRIME },
+ { "AKA", AKA_SIM_METHOD_HINT_AKA },
+ { "SIM", AKA_SIM_METHOD_HINT_SIM },
+ { NULL }
};
-size_t sim_id_method_hint_table_len = NUM_ELEMENTS(sim_id_method_hint_table);
+size_t fr_aka_sim_id_method_table_len = NUM_ELEMENTS(fr_aka_sim_id_method_table);
/** Find where the identity ends
*
* @return
* - How long the identity portion of the NAI is.
*/
-size_t fr_sim_id_user_len(char const *nai, size_t nai_len)
+size_t fr_aka_sim_id_user_len(char const *nai, size_t nai_len)
{
char const *p;
* - A pointer to where the domain portion of the domain starts.
* - NULL if there was no @ in the identity.
*/
-char const *fr_sim_domain(char const *nai, size_t nai_len)
+char const *fr_aka_sim_domain(char const *nai, size_t nai_len)
{
char const *p;
* - number of bytes parsed.
* - <= 0 on error - The negative offset of where parsing failed.
*/
-ssize_t fr_sim_3gpp_root_nai_domain_mcc_mnc(uint16_t *mnc, uint16_t *mcc,
- char const *domain, size_t domain_len)
+ssize_t fr_aka_sim_3gpp_root_nai_domain_mcc_mnc(uint16_t *mnc, uint16_t *mcc,
+ char const *domain, size_t domain_len)
{
char const *p = domain, *end = p + domain_len;
char *q;
return p - domain;
}
- /** Determine what type of ID was provided in the initial identity response
- *
- * @param[out] hint Whether this is a hint to do EAP-SIM or EAP-AKA[']:
- * - SIM_METHOD_HINT_AKA_PRIME this ID was generated during an EAP-AKA' exchange
- * or the supplicant hints it wants to perform EAP-AKA'.
- * - SIM_METHOD_HINT_AKA this ID was generated during an EAP-AKA exchange
- * or the supplicant hints it wants to perform EAP-AKA.
- * - SIM_METHOD_HINT_SIM this IS was generated during an EAP-SIM exchange
- * or the supplicant hints it wants to perform EAP-SIM.
- * - SIM_METHOD_HINT_UNKNOWN we don't know what type of authentication generated
- * this ID or which one to start.
- * @param[out] type What type of identity this is:
- * - SIM_ID_TYPE_PERMANENT if the ID is an IMSI.
- * - SIM_ID_TYPE_PSEUDONYM if the ID is a freeform pseudonym.
- * - SIM_ID_TYPE_FASTAUTH if the ID is a fastauth identity.
- * - SIM_ID_TYPE_UNKNOWN if we can't determine what sort of ID this is.
- * @param[in] id the NAI string provided.
- * @param[in] id_len the length of the NAI string.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-int fr_sim_id_type(fr_sim_id_type_t *type, fr_sim_method_hint_t *hint, char const *id, size_t id_len)
+/** Determine what type of ID was provided in the initial identity response
+ *
+ * @param[out] hint Whether this is a hint to do EAP-SIM or EAP-AKA[']:
+ * - AKA_SIM_METHOD_HINT_AKA_PRIME this ID was generated during an EAP-AKA' exchange
+ * or the supplicant hints it wants to perform EAP-AKA'.
+ * - AKA_SIM_METHOD_HINT_AKA this ID was generated during an EAP-AKA exchange
+ * or the supplicant hints it wants to perform EAP-AKA.
+ * - AKA_SIM_METHOD_HINT_SIM this IS was generated during an EAP-SIM exchange
+ * or the supplicant hints it wants to perform EAP-SIM.
+ * - AKA_SIM_METHOD_HINT_UNKNOWN we don't know what type of authentication generated
+ * this ID or which one to start.
+ * @param[out] type What type of identity this is:
+ * - AKA_SIM_ID_TYPE_PERMANENT if the ID is an IMSI.
+ * - AKA_SIM_ID_TYPE_PSEUDONYM if the ID is a freeform pseudonym.
+ * - AKA_SIM_ID_TYPE_FASTAUTH if the ID is a fastauth identity.
+ * - AKA_SIM_ID_TYPE_UNKNOWN if we can't determine what sort of ID this is.
+ * @param[in] id the NAI string provided.
+ * @param[in] id_len the length of the NAI string.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int fr_aka_sim_id_type(fr_aka_sim_id_type_t *type, fr_aka_sim_method_hint_t *hint, char const *id, size_t id_len)
{
size_t i;
if (id_len < 1) {
- *hint = SIM_METHOD_HINT_UNKNOWN;
- *type = SIM_ID_TYPE_UNKNOWN;
+ *hint = AKA_SIM_METHOD_HINT_UNKNOWN;
+ *type = AKA_SIM_ID_TYPE_UNKNOWN;
fr_strerror_printf("ID length too short");
return -1;
}
* entire string if no '@' found, so works with
* full NAI strings and Stripped-User-Name.
*/
- id_len = fr_sim_id_user_len(id, id_len);
+ id_len = fr_aka_sim_id_user_len(id, id_len);
/*
* Permanent ID format check
*/
switch (id[0]) {
- case SIM_ID_TAG_PERMANENT_SIM:
- case SIM_ID_TAG_PERMANENT_AKA:
- case SIM_ID_TAG_PERMANENT_AKA_PRIME:
+ case ID_TAG_SIM_PERMANENT:
+ case ID_TAG_AKA_PERMANENT:
+ case ID_TAG_AKA_PRIME_PERMANENT:
if (id_len > 16) {
fr_strerror_printf("IMSI too long, expected <= 16 bytes got %zu bytes", id_len);
goto bad_format;
for (i = 1; i < id_len; i++) {
if (!isdigit(id[i])) {
- fr_strerror_printf("Invalid digit '%c' in IMSI \"%.*s\"", id[i], (int)id_len, id);
+ fr_strerror_printf("Invalid digit '%pV' in IMSI \"%pV\"",
+ fr_box_strvalue_len(&id[i], 1),
+ fr_box_strvalue_len(id, id_len));
goto bad_format;
}
}
switch (id[0]) {
- case SIM_ID_TAG_PERMANENT_SIM:
- *hint = SIM_METHOD_HINT_SIM;
- *type = SIM_ID_TYPE_PERMANENT; /* All digits */
+ case ID_TAG_SIM_PERMANENT:
+ *hint = AKA_SIM_METHOD_HINT_SIM;
+ *type = AKA_SIM_ID_TYPE_PERMANENT; /* All digits */
return 0;
- case SIM_ID_TAG_PERMANENT_AKA:
- *hint = SIM_METHOD_HINT_AKA;
- *type = SIM_ID_TYPE_PERMANENT; /* All digits */
+ case ID_TAG_AKA_PERMANENT:
+ *hint = AKA_SIM_METHOD_HINT_AKA;
+ *type = AKA_SIM_ID_TYPE_PERMANENT; /* All digits */
return 0;
- case SIM_ID_TAG_PERMANENT_AKA_PRIME:
- *hint = SIM_METHOD_HINT_AKA_PRIME;
- *type = SIM_ID_TYPE_PERMANENT; /* All Digits */
+ case ID_TAG_AKA_PRIME_PERMANENT:
+ *hint = AKA_SIM_METHOD_HINT_AKA_PRIME;
+ *type = AKA_SIM_ID_TYPE_PERMANENT; /* All Digits */
return 0;
default:
* Pseudonym
*/
switch (id[0]) {
- case SIM_ID_TAG_PSEUDONYM_SIM:
- *hint = SIM_METHOD_HINT_SIM;
- *type = SIM_ID_TYPE_PSEUDONYM;
+ case ID_TAG_SIM_PSEUDONYM:
+ *hint = AKA_SIM_METHOD_HINT_SIM;
+ *type = AKA_SIM_ID_TYPE_PSEUDONYM;
return 0;
- case SIM_ID_TAG_PSEUDONYM_AKA:
- *hint = SIM_METHOD_HINT_AKA;
- *type = SIM_ID_TYPE_PSEUDONYM;
+ case ID_TAG_AKA_PSEUDONYM:
+ *hint = AKA_SIM_METHOD_HINT_AKA;
+ *type = AKA_SIM_ID_TYPE_PSEUDONYM;
return 0;
- case SIM_ID_TAG_PSEUDONYM_AKA_PRIME:
- *hint = SIM_METHOD_HINT_AKA_PRIME;
- *type = SIM_ID_TYPE_PSEUDONYM;
+ case ID_TAG_AKA_PRIME_PSEUDONYM:
+ *hint = AKA_SIM_METHOD_HINT_AKA_PRIME;
+ *type = AKA_SIM_ID_TYPE_PSEUDONYM;
return 0;
/*
* Fast reauth identity
*/
- case SIM_ID_TAG_FASTAUTH_SIM:
- *hint = SIM_METHOD_HINT_SIM;
- *type = SIM_ID_TYPE_FASTAUTH;
+ case ID_TAG_SIM_FASTAUTH:
+ *hint = AKA_SIM_METHOD_HINT_SIM;
+ *type = AKA_SIM_ID_TYPE_FASTAUTH;
return 0;
- case SIM_ID_TAG_FASTAUTH_AKA:
- *hint = SIM_METHOD_HINT_AKA;
- *type = SIM_ID_TYPE_FASTAUTH;
+ case ID_TAG_AKA_FASTAUTH:
+ *hint = AKA_SIM_METHOD_HINT_AKA;
+ *type = AKA_SIM_ID_TYPE_FASTAUTH;
return 0;
- case SIM_ID_TAG_FASTAUTH_AKA_PRIME:
- *hint = SIM_METHOD_HINT_AKA_PRIME;
- *type = SIM_ID_TYPE_FASTAUTH;
+ case ID_TAG_AKA_PRIME_FASTAUTH:
+ *hint = AKA_SIM_METHOD_HINT_AKA_PRIME;
+ *type = AKA_SIM_ID_TYPE_FASTAUTH;
return 0;
- case SIM_ID_TAG_PERMANENT_SIM:
- *hint = SIM_METHOD_HINT_UNKNOWN;
- *type = SIM_ID_TYPE_UNKNOWN;
+ case ID_TAG_SIM_PERMANENT:
+ *hint = AKA_SIM_METHOD_HINT_UNKNOWN;
+ *type = AKA_SIM_ID_TYPE_UNKNOWN;
fr_strerror_printf_push("Got SIM-Permanent-ID tag, but identity is not a permanent ID");
return -1;
- case SIM_ID_TAG_PERMANENT_AKA:
- *hint = SIM_METHOD_HINT_UNKNOWN;
- *type = SIM_ID_TYPE_UNKNOWN;
+ case ID_TAG_AKA_PERMANENT:
+ *hint = AKA_SIM_METHOD_HINT_UNKNOWN;
+ *type = AKA_SIM_ID_TYPE_UNKNOWN;
fr_strerror_printf_push("Got AKA-Permanent-ID tag, but identity is not a permanent ID");
return -1;
default:
- *hint = SIM_METHOD_HINT_UNKNOWN;
- *type = SIM_ID_TYPE_UNKNOWN;
- fr_strerror_printf_push("Unrecognised tag '%pV'", fr_box_strvalue_len(id, 1)); /* Have to box, may be \000 */
+ *hint = AKA_SIM_METHOD_HINT_UNKNOWN;
+ *type = AKA_SIM_ID_TYPE_UNKNOWN;
+ fr_strerror_printf_push("Unrecognised tag '%pV'", fr_box_strvalue_len(id, 1));
return -1;
}
}
+static char hint_byte_matrix[AKA_SIM_METHOD_HINT_MAX][AKA_SIM_ID_TYPE_MAX] = {
+ [AKA_SIM_METHOD_HINT_SIM] = {
+ [AKA_SIM_ID_TYPE_PERMANENT] = ID_TAG_SIM_PERMANENT,
+ [AKA_SIM_ID_TYPE_PSEUDONYM] = ID_TAG_SIM_PSEUDONYM,
+ [AKA_SIM_ID_TYPE_FASTAUTH] = ID_TAG_SIM_PERMANENT,
+ [AKA_SIM_ID_TYPE_UNKNOWN] = '\0',
+ },
+ [AKA_SIM_METHOD_HINT_AKA] = {
+ [AKA_SIM_ID_TYPE_PERMANENT] = ID_TAG_AKA_PERMANENT,
+ [AKA_SIM_ID_TYPE_PSEUDONYM] = ID_TAG_AKA_PSEUDONYM,
+ [AKA_SIM_ID_TYPE_FASTAUTH] = ID_TAG_AKA_PERMANENT,
+ [AKA_SIM_ID_TYPE_UNKNOWN] = '\0',
+ },
+ [AKA_SIM_METHOD_HINT_AKA_PRIME] = {
+ [AKA_SIM_ID_TYPE_PERMANENT] = ID_TAG_AKA_PRIME_PERMANENT,
+ [AKA_SIM_ID_TYPE_PSEUDONYM] = ID_TAG_AKA_PRIME_PSEUDONYM,
+ [AKA_SIM_ID_TYPE_FASTAUTH] = ID_TAG_AKA_PRIME_PERMANENT,
+ [AKA_SIM_ID_TYPE_UNKNOWN] = '\0',
+ },
+ [AKA_SIM_METHOD_HINT_UNKNOWN] = {
+ '\0' /* Should set for all elements */
+ }
+};
+
+/** Return the expected identity hint for a given type/method combination
+ *
+ * @param[in] type Whether this is a permanent, pseudonym or fastauth ID
+ * @param[in] method What EAP-Method the identity hints at.
+ * @return
+ * - An IMSI tag byte [0-9] (ASCII)
+ * - '\0' if either the method or type values are unknown.
+ */
+char fr_aka_sim_hint_byte(fr_aka_sim_id_type_t type, fr_aka_sim_method_hint_t method)
+{
+ return hint_byte_matrix[method][type];
+}
+
/** Create a 3gpp pseudonym from a permanent ID
*
* @param[out] out Where to write the resulting pseudonym, must be a buffer of
- * exactly SIM_3GPP_PSEUDONYM_LEN + 1 bytes.
+ * exactly AKA_SIM_3GPP_PSEUDONYM_LEN + 1 bytes.
* @param[in] imsi Permanent ID to derive pseudonym from. Note: If the IMSI is less than
* 15 digits it will be rpadded with zeros.
* @param[in] imsi_len Length of the IMSI. Must be between 1-15.
* - 0 on success.
* - -1 if any of the parameters were invalid.
*/
-int fr_sim_id_3gpp_pseudonym_encrypt(char out[SIM_3GPP_PSEUDONYM_LEN + 1],
- char const *imsi, size_t imsi_len,
- uint8_t tag, uint8_t key_ind, uint8_t const key[16])
+int fr_aka_sim_id_3gpp_pseudonym_encrypt(char out[AKA_SIM_3GPP_PSEUDONYM_LEN + 1],
+ char const *imsi, size_t imsi_len,
+ uint8_t tag, uint8_t key_ind, uint8_t const key[16])
{
uint8_t padded[16]; /* Random (8 bytes) + Compressed (8 bytes) */
uint8_t encr[16]; /* aes_ecb(padded) */
* Technically the IMSI number is between 14-15, but this
* encryption scheme only works for 15 char IMSIs.
*/
- if (unlikely(imsi_len != SIM_IMSI_MAX_LEN)) {
+ if (unlikely(imsi_len != AKA_SIM_IMSI_MAX_LEN)) {
fr_strerror_printf("Invalid ID len, expected length of 15, got %zu", imsi_len);
return -1;
}
*out_p++ = fr_base64_str[u_p[2] & 0x3f]; /* 6 low bits of p[2] */
u_p += 3;
}
- if ((out_p - out) != SIM_3GPP_PSEUDONYM_LEN) {
+ if ((out_p - out) != AKA_SIM_3GPP_PSEUDONYM_LEN) {
fr_strerror_printf("Base64 output length invalid, expected %i bytes, got %zu bytes",
- SIM_3GPP_PSEUDONYM_LEN, out_p - out);
+ AKA_SIM_3GPP_PSEUDONYM_LEN, out_p - out);
return -1;
}
- out[SIM_3GPP_PSEUDONYM_LEN] = '\0';
+ out[AKA_SIM_3GPP_PSEUDONYM_LEN] = '\0';
return 0;
}
*
* @return the tag associated with the pseudonym.
*/
-uint8_t fr_sim_id_3gpp_pseudonym_tag(char const encr_id[SIM_3GPP_PSEUDONYM_LEN])
+uint8_t fr_aka_sim_id_3gpp_pseudonym_tag(char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN])
{
return fr_base64_sextet[us(encr_id[0])];
}
*
* @return the key index associated with the pseudonym.
*/
-uint8_t fr_sim_id_3gpp_pseudonym_key_index(char const encr_id[SIM_3GPP_PSEUDONYM_LEN])
+uint8_t fr_aka_sim_id_3gpp_pseudonym_key_index(char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN])
{
return ((fr_base64_sextet[us(encr_id[1])] & 0x3c) >> 2);
}
* - 0 on success.
* - -1 if any of the parameters were invalid.
*/
-int fr_sim_id_3gpp_pseudonym_decrypt(char out[SIM_IMSI_MAX_LEN + 1],
- char const encr_id[SIM_3GPP_PSEUDONYM_LEN], uint8_t const key[16])
+int fr_aka_sim_id_3gpp_pseudonym_decrypt(char out[AKA_SIM_IMSI_MAX_LEN + 1],
+ char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN], uint8_t const key[16])
{
EVP_CIPHER_CTX *evp_ctx;
uint8_t *compressed = decr + 8; /* Pointer into plaintext after the random component */
size_t decr_len;
- char const *p = encr_id, *end = p + SIM_3GPP_PSEUDONYM_LEN;
+ char const *p = encr_id, *end = p + AKA_SIM_3GPP_PSEUDONYM_LEN;
size_t len = 0;
int i;
- for (i = 0; i < SIM_3GPP_PSEUDONYM_LEN; i++) {
+ for (i = 0; i < AKA_SIM_3GPP_PSEUDONYM_LEN; i++) {
if (!fr_is_base64(encr_id[i])) {
- fr_strerror_printf("Encrypted IMSI contains non-base64 char '%c'", encr_id[i]);
+ fr_strerror_printf("Encrypted IMSI contains non-base64 char '%pV'",
+ fr_box_strvalue_len(&encr_id[i], 1));
return -1;
}
}
* This should never happen, and probably means that
* some sort of memory corruption has occured.
*/
- if (unlikely(decr_len > (SIM_IMSI_MAX_LEN + 1))) {
+ if (unlikely(decr_len > (AKA_SIM_IMSI_MAX_LEN + 1))) {
fr_strerror_printf("Decrypted data len invalid. Expected %i bytes, got %zu bytes",
- (SIM_IMSI_MAX_LEN + 1), decr_len);
+ (AKA_SIM_IMSI_MAX_LEN + 1), decr_len);
goto error;
}
EVP_CIPHER_CTX_free(evp_ctx);
- out[SIM_IMSI_MAX_LEN] = '\0';
+ out[AKA_SIM_IMSI_MAX_LEN] = '\0';
return 0;
}
uint8_t key_ind;
char const *log;
- char encrypted_id[SIM_3GPP_PSEUDONYM_LEN + 1];
+ char encrypted_id[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
char decrypted_id[sizeof(id)];
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 6, 0, (uint8_t const *)key) == 0);
+ TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 6, 0, (uint8_t const *)key) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
- tag = fr_sim_id_3gpp_pseudonym_tag(encrypted_id);
+ tag = fr_aka_sim_id_3gpp_pseudonym_tag(encrypted_id);
TEST_CHECK(tag == 6);
- key_ind = fr_sim_id_3gpp_pseudonym_key_index(encrypted_id);
+ key_ind = fr_aka_sim_id_3gpp_pseudonym_key_index(encrypted_id);
TEST_CHECK(key_ind == 0);
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
+ TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
uint8_t key_ind;
char const *log;
- char encrypted_id[SIM_3GPP_PSEUDONYM_LEN + 1];
+ char encrypted_id[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
char decrypted_id[sizeof(id)];
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 11, 1, (uint8_t const *)key) == 0);
+ TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1, 11, 1, (uint8_t const *)key) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
- tag = fr_sim_id_3gpp_pseudonym_tag(encrypted_id);
+ tag = fr_aka_sim_id_3gpp_pseudonym_tag(encrypted_id);
TEST_CHECK(tag == 11);
- key_ind = fr_sim_id_3gpp_pseudonym_key_index(encrypted_id);
+ key_ind = fr_aka_sim_id_3gpp_pseudonym_key_index(encrypted_id);
TEST_CHECK(key_ind == 1);
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
+ TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)key) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
uint8_t key_ind;
char const *log;
- char encrypted_id[SIM_3GPP_PSEUDONYM_LEN + 1];
+ char encrypted_id[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
char decrypted_id[sizeof(id)];
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1,
- 9, 15, (uint8_t const *)keys[15]) == 0);
+ TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted_id, id, sizeof(id) - 1,
+ 9, 15, (uint8_t const *)keys[15]) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
- tag = fr_sim_id_3gpp_pseudonym_tag(encrypted_id);
+ tag = fr_aka_sim_id_3gpp_pseudonym_tag(encrypted_id);
TEST_CHECK(tag == 9);
- key_ind = fr_sim_id_3gpp_pseudonym_key_index(encrypted_id);
+ key_ind = fr_aka_sim_id_3gpp_pseudonym_key_index(encrypted_id);
TEST_CHECK(key_ind == 15);
- TEST_CHECK(fr_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)keys[key_ind]) == 0);
+ TEST_CHECK(fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted_id, encrypted_id, (uint8_t const *)keys[key_ind]) == 0);
while ((log = fr_strerror_pop())) printf("%s\n", log);
TEST_CHECK(memcmp(id, decrypted_id, 15) == 0);
--- /dev/null
+#pragma once
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file src/lib/aka-sim/id.h
+ * @brief EAP-SIM/EAP-AKA identity detection, creation, and decyption.
+ *
+ * @copyright 2017 The FreeRADIUS server project
+ */
+#include <sys/types.h>
+#include <freeradius-devel/util/token.h>
+
+#define AKA_SIM_3GPP_PSEUDONYM_LEN 23 //!< Length of a base64 encoded 3gpp pseudonym.
+#define AKA_SIM_IMSI_MAX_LEN 15 //!< Length of an IMSI number in ASCII.
+#define AKA_SIM_IMSI_MIN_LEN 14 //!< Minimum length of an IMSI number in ASCII.
+
+/** SIM/AKA method hints
+ *
+ * Derived from processing the provided identity.
+ */
+typedef enum {
+ AKA_SIM_METHOD_HINT_UNKNOWN = 0, //!< We don't know what method the identity hints at.
+ AKA_SIM_METHOD_HINT_SIM, //!< The identity hints the supplicant wants to use
+ ///< EAP-SIM.
+ AKA_SIM_METHOD_HINT_AKA, //!< The identity hints the supplicant wants to use
+ ///< EAP-AKA.
+ AKA_SIM_METHOD_HINT_AKA_PRIME,
+ AKA_SIM_METHOD_HINT_MAX
+} fr_aka_sim_method_hint_t;
+
+/** SIM/AKA identity type hints
+ *
+ * Derived from the processing the provided identity.
+ */
+typedef enum {
+ AKA_SIM_ID_TYPE_UNKNOWN = 0, //!< We don't know what type of identity this is.
+ AKA_SIM_ID_TYPE_PERMANENT, //!< This is a permanent identity (the IMSI of the SIM).
+ AKA_SIM_ID_TYPE_PSEUDONYM, //!< This is a custom pseudonym.
+ AKA_SIM_ID_TYPE_FASTAUTH, //!< This is a fastauth (session-resumption) id.
+ AKA_SIM_ID_TYPE_MAX
+} fr_aka_sim_id_type_t;
+
+typedef enum {
+ ID_TAG_SIM_PERMANENT = '1', //!< IMSI, and hint that client wants to do EAP-SIM
+ ID_TAG_SIM_PSEUDONYM = '3', //!< Pseudonym, continue EAP-SIM
+ ID_TAG_SIM_FASTAUTH = '5', //!< Fastauth, continue EAP-SIM
+
+ ID_TAG_AKA_PERMANENT = '0', //!< IMSI, and hint that client wants to do EAP-AKA
+ ID_TAG_AKA_PSEUDONYM = '2', //!< Pseudonym, continue EAP-AKA
+ ID_TAG_AKA_FASTAUTH = '4', //!< Fastauth, continue EAP-AKA
+
+ ID_TAG_AKA_PRIME_PERMANENT = '6', //!< IMSI, and hint that client wants to do EAP-AKA-Prime.
+ ID_TAG_AKA_PRIME_PSEUDONYM = '7', //!< Pseudonym, continue EAP-AKA-Prime
+ ID_TAG_AKA_PRIME_FASTAUTH = '8' //!< Fastuath, continue EAP-AKA-Prime
+} fr_aka_sim_id_tag_t;
+
+/** Identity request types
+ */
+typedef enum {
+ AKA_SIM_NO_ID_REQ = 0, //!< We're not requesting any ID.
+ AKA_SIM_ANY_ID_REQ, //!< Request IMSI, Pseudonym or Fast-reauth.
+ AKA_SIM_FULLAUTH_ID_REQ, //!< Request IMSI or Pseudonym.
+ AKA_SIM_PERMANENT_ID_REQ, //!< Request IMSI.
+} fr_aka_sim_id_req_type_t;
+
+extern fr_table_num_sorted_t const fr_aka_sim_id_request_table[];
+extern size_t fr_aka_sim_id_request_table_len;
+extern fr_table_num_sorted_t const fr_aka_sim_id_method_table[];
+extern size_t fr_aka_sim_id_method_table_len;
+
+#define ID_TAG_SIM_PSEUDONYM_B64 55
+#define ID_TAG_AKA_PSEUDONYM_B64 54
+#define ID_TAG_AKA_PRIME_PSEUDONYM_B64 59
+
+size_t fr_aka_sim_id_user_len(char const *nai, size_t nai_len);
+
+char const *fr_aka_sim_domain(char const *nai, size_t nai_len);
+
+ssize_t fr_aka_sim_3gpp_root_nai_domain_mcc_mnc(uint16_t *mnc, uint16_t *mcc,
+ char const *domain, size_t domain_len);
+
+int fr_aka_sim_id_type(fr_aka_sim_id_type_t *type, fr_aka_sim_method_hint_t *hint,
+ char const *id, size_t id_len);
+
+char fr_aka_sim_hint_byte(fr_aka_sim_id_type_t type, fr_aka_sim_method_hint_t method);
+
+int fr_aka_sim_id_3gpp_pseudonym_encrypt(char out[AKA_SIM_3GPP_PSEUDONYM_LEN + 1],
+ char const *imsi, size_t imsi_len,
+ uint8_t tag, uint8_t key_ind, uint8_t const key[16]);
+
+uint8_t fr_aka_sim_id_3gpp_pseudonym_tag(char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN]);
+
+uint8_t fr_aka_sim_id_3gpp_pseudonym_key_index(char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN]);
+
+int fr_aka_sim_id_3gpp_pseudonym_decrypt(char out[AKA_SIM_IMSI_MAX_LEN],
+ char const encr_id[AKA_SIM_3GPP_PSEUDONYM_LEN],
+ uint8_t const key[16]);
--- /dev/null
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file src/lib/eap_aka_sim/state_machine.c
+ * @brief Implement a common state machine for EAP-SIM, EAP-AKA, EAP-AKA'.
+ *
+ * @author Arran Cudbard-Bell \<a.cudbardb@freeradius.org\>
+ *
+ * @copyright 2019 The FreeRADIUS server project
+ * @copyright 2019 Network RADIUS \<info@networkradius.com\>
+ */
+RCSID("$Id$")
+#include <freeradius-devel/eap/base.h>
+#include <freeradius-devel/eap/types.h>
+#include <freeradius-devel/unlang/module.h>
+
+#include "base.h"
+#include "state_machine.h"
+#include "attrs.h"
+
+#ifndef EAP_TLS_MPPE_KEY_LEN
+# define EAP_TLS_MPPE_KEY_LEN 32
+#endif
+
+/** A state transition function
+ *
+ * This is passed to sub-state machines that perform other actions, before
+ * fully transitioning to a new state.
+ *
+ * Examples of these are the sub-state machines that deal with clearing
+ * pseudonyms and reauthentication data.
+ */
+typedef rlm_rcode_t(*aka_sim_state_enter_t)(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session);
+
+static rlm_rcode_t common_eap_failure(void *instance, void *thread, REQUEST *request);
+static rlm_rcode_t common_failure_notification(void *instance, void *thread, REQUEST *request);
+static rlm_rcode_t common_eap_success(void *instance, void *thread, REQUEST *request);
+static rlm_rcode_t common_success_notification(void *instance, void *thread, REQUEST *request);
+static rlm_rcode_t common_reauthentication(void *instance, void *thread, REQUEST *request);
+static rlm_rcode_t aka_challenge(void *instance, void *thread, REQUEST *request);
+static rlm_rcode_t sim_challenge(void *instance, void *thread, REQUEST *request);
+static rlm_rcode_t aka_identity(void *instance, void *thread, REQUEST *request);
+static rlm_rcode_t sim_start(void *instance, void *thread, REQUEST *request);
+
+static rlm_rcode_t common_failure_notification_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session);
+static rlm_rcode_t aka_challenge_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session);
+static rlm_rcode_t sim_challenge_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session);
+static rlm_rcode_t common_challenge_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session);
+static rlm_rcode_t aka_identity_enter(eap_aka_sim_common_conf_t *inst, REQUEST *request, eap_session_t *eap_session);
+static rlm_rcode_t sim_start_enter(eap_aka_sim_common_conf_t *inst, REQUEST *request, eap_session_t *eap_session);
+static rlm_rcode_t common_identity_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session);
+
+static module_state_func_table_t const aka_sim_stable_table[] = {
+ { "FAILURE-NOTIFICATION", common_failure_notification },
+ { "EAP-FAILURE", common_eap_failure },
+ { "SUCCESS-NOTIFICATION", common_success_notification },
+ { "EAP-SUCCESS", common_eap_success },
+ { "REAUTHENTICATION", common_reauthentication },
+ { "AKA-CHALLENGE", aka_challenge },
+ { "SIM-CHALLENGE", sim_challenge },
+ { "IDENTITY", aka_identity },
+ { "SIM-START", sim_start },
+ { "EAP-IDENTITY", aka_sim_state_machine_start },
+
+ { NULL }
+};
+
+/** Cancel a call to a submodule
+ *
+ * @param[in] instance UNUSED.
+ * @param[in] thread UNUSED.
+ * @param[in] request The current request.
+ * @param[in] rctx the eap_session_t
+ * @param[in] action to perform.
+ */
+static void mod_signal(UNUSED void *instance, UNUSED void *thread, REQUEST *request, UNUSED void *rctx,
+ fr_state_signal_t action)
+{
+ eap_session_t *eap_session = eap_session_get(request->parent);
+
+ if (action != FR_SIGNAL_CANCEL) return;
+
+ RDEBUG2("Request cancelled - Destroying session");
+
+ TALLOC_FREE(eap_session->opaque);
+}
+
+/** Warn the user that the rcode they provided is being ignored in this section
+ *
+ */
+static inline void section_rcode_ignored(REQUEST *request)
+{
+ switch (request->rcode) {
+ case RLM_MODULE_USER_SECTION_REJECT:
+ RWDEBUG("Ignoring rcode (%s)",
+ fr_table_str_by_value(mod_rcode_table, request->rcode, "<invalid>"));
+ break;
+
+ default:
+ break;
+ }
+}
+
+/** Trigger a state transition to FAILURE-NOTIFICATION if the section returned a failure code
+ *
+ */
+#define section_rcode_process(_inst, _request, _eap_session, _eap_aka_sim_session) \
+{ \
+ if (after_authentication(_eap_aka_sim_session)) { \
+ switch ((_request)->rcode) { \
+ case RLM_MODULE_REJECT: \
+ case RLM_MODULE_USERLOCK: \
+ eap_aka_sim_session->failure_type = FR_NOTIFICATION_VALUE_TEMPORARILY_DENIED; \
+ return common_failure_notification_enter(_inst, _request, _eap_session); \
+ case RLM_MODULE_NOTFOUND: \
+ eap_aka_sim_session->failure_type = FR_NOTIFICATION_VALUE_NOT_SUBSCRIBED; \
+ return common_failure_notification_enter(_inst, _request, _eap_session); \
+ case RLM_MODULE_INVALID: \
+ case RLM_MODULE_FAIL: \
+ eap_aka_sim_session->failure_type = FR_NOTIFICATION_VALUE_GENERAL_FAILURE_AFTER_AUTHENTICATION;\
+ return common_failure_notification_enter(_inst, _request, _eap_session); \
+ default: \
+ break; \
+ } \
+ } else { \
+ switch ((_request)->rcode) { \
+ case RLM_MODULE_USER_SECTION_REJECT: \
+ REDEBUG("Section rcode (%s) indicates we should reject the user", \
+ fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>")); \
+ return common_failure_notification_enter(_inst, _request, _eap_session); \
+ default: \
+ break; \
+ } \
+ } \
+}
+
+/** Sync up what identity we're requesting with attributes in the reply
+ *
+ */
+static bool identity_req_set_by_user(REQUEST *request, eap_aka_sim_session_t *eap_aka_sim_session)
+{
+ VALUE_PAIR *vp;
+ fr_cursor_t cursor;
+ bool set_by_user = false;
+
+ /*
+ * Check if the user included any of the
+ * ID req attributes. If they did, use
+ * them to inform what we do next, and
+ * then delete them so they don't screw
+ * up any of the other code.
+ */
+ for (vp = fr_cursor_init(&cursor, &request->reply->vps);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
+ if (vp->da == attr_eap_aka_sim_permanent_id_req) {
+ eap_aka_sim_session->id_req = AKA_SIM_PERMANENT_ID_REQ;
+ found:
+ set_by_user = true;
+ RDEBUG2("Previous section added &reply:%pP, will request additional identity", vp);
+ fr_cursor_free_item(&cursor);
+ }
+ else if (vp->da == attr_eap_aka_sim_fullauth_id_req) {
+ eap_aka_sim_session->id_req = AKA_SIM_FULLAUTH_ID_REQ;
+ goto found;
+ }
+ else if (vp->da == attr_eap_aka_sim_any_id_req) {
+ eap_aka_sim_session->id_req = AKA_SIM_ANY_ID_REQ;
+ goto found;
+ }
+ }
+
+ return set_by_user;
+}
+
+/** Based on the hint byte in the identity, add &Identity-Type and &Method-Hint attributes
+ *
+ */
+static void identity_hint_pairs_add(fr_aka_sim_id_type_t *type_p, fr_aka_sim_method_hint_t *method_p,
+ REQUEST *request, char const *identity)
+{
+ fr_aka_sim_id_type_t type;
+ fr_aka_sim_method_hint_t method;
+
+ /*
+ * Process the identity that we received.
+ */
+ if (fr_aka_sim_id_type(&type, &method,
+ identity, talloc_array_length(identity) - 1) < 0) {
+ RPWDEBUG2("Failed parsing identity, continuing anyway");
+ }
+
+ /*
+ * Map the output from the generic ID parser
+ * function to specific EAP-AKA internal
+ * attributes in the subrequest.
+ */
+ if (type != AKA_SIM_ID_TYPE_UNKNOWN) {
+ VALUE_PAIR *vp = NULL;
+
+ MEM(pair_update_request(&vp, attr_eap_aka_sim_identity_type) >= 0);
+ switch (type) {
+ case AKA_SIM_ID_TYPE_PERMANENT:
+ vp->vp_uint32 = FR_IDENTITY_TYPE_VALUE_PERMANENT;
+ break;
+
+ case AKA_SIM_ID_TYPE_PSEUDONYM:
+ vp->vp_uint32 = FR_IDENTITY_TYPE_VALUE_PSEUDONYM;
+ break;
+
+ case AKA_SIM_ID_TYPE_FASTAUTH:
+ vp->vp_uint32 = FR_IDENTITY_TYPE_VALUE_FASTAUTH;
+ break;
+
+ default:
+ rad_assert(0);
+ }
+ }
+
+ /*
+ * Map the output from the generic ID parser
+ * function to specific EAP-AKA internal
+ * attributes in the subrequest.
+ */
+ if (method != AKA_SIM_METHOD_HINT_UNKNOWN) {
+ VALUE_PAIR *vp = NULL;
+
+ MEM(pair_update_request(&vp, attr_eap_aka_sim_method_hint) >= 0);
+ switch (method) {
+ case AKA_SIM_METHOD_HINT_AKA_PRIME:
+ vp->vp_uint32 = FR_METHOD_HINT_VALUE_AKA_PRIME;
+ break;
+
+ case AKA_SIM_METHOD_HINT_AKA:
+ vp->vp_uint32 = FR_METHOD_HINT_VALUE_AKA;
+ break;
+
+ case AKA_SIM_METHOD_HINT_SIM:
+ vp->vp_uint32 = FR_METHOD_HINT_VALUE_SIM;
+ break;
+
+ default:
+ rad_assert(0);
+ }
+ }
+
+ if (type_p) *type_p = type;
+ if (method_p) *method_p = method;
+}
+
+/** Print out the error the client returned
+ *
+ */
+static inline void client_error_debug(REQUEST *request, VALUE_PAIR *from_peer)
+{
+ VALUE_PAIR *vp;
+
+ vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_client_error_code, TAG_ANY);
+ if (!vp) {
+ REDEBUG("Peer has not supplied a AT_ERROR_CODE");
+ } else {
+ REDEBUG("Peer rejected request with error: %i (%pV)", vp->vp_uint16, &vp->data);
+ }
+}
+
+/** Add an Identity Request attribute to the reply
+ *
+ * Verify the progression of identity requests is valid.
+ *
+ * @param[in] request The current request.
+ * @param[in] eap_aka_sim_session The current eap_aka_sim_session.
+ * @return
+ * - 0 on success.
+ * - -1 on failure (progression of identities was not valid).
+ */
+static int identity_req_pairs_add(REQUEST *request, eap_aka_sim_session_t *eap_aka_sim_session)
+{
+ VALUE_PAIR *vp;
+
+ switch (eap_aka_sim_session->id_req) {
+ case AKA_SIM_ANY_ID_REQ:
+ if (eap_aka_sim_session->last_id_req != AKA_SIM_NO_ID_REQ) {
+ id_out_of_order:
+ REDEBUG("Cannot send %s, already sent %s",
+ fr_table_str_by_value(fr_aka_sim_id_request_table, eap_aka_sim_session->id_req, "<INVALID>"),
+ fr_table_str_by_value(fr_aka_sim_id_request_table, eap_aka_sim_session->last_id_req, "<INVALID>"));
+ return -1;
+ }
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_any_id_req) >= 0);
+ vp->vp_bool = true;
+ break;
+
+ case AKA_SIM_FULLAUTH_ID_REQ:
+ switch (eap_aka_sim_session->last_id_req) {
+ case AKA_SIM_NO_ID_REQ: /* Not sent anything before */
+ case AKA_SIM_ANY_ID_REQ: /* Last request was for any ID, but the re-auth ID was bad */
+ break;
+
+ default:
+ goto id_out_of_order;
+ }
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_fullauth_id_req) >= 0);
+ vp->vp_bool = true;
+ break;
+
+ case AKA_SIM_PERMANENT_ID_REQ:
+ switch (eap_aka_sim_session->last_id_req) {
+ case AKA_SIM_NO_ID_REQ: /* Not sent anything before */
+ case AKA_SIM_ANY_ID_REQ: /* Last request was for any ID, but the re-auth ID was bad */
+ case AKA_SIM_FULLAUTH_ID_REQ: /* ...didn't understand the pseudonym either */
+ break;
+
+ default:
+ goto id_out_of_order;
+ }
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_permanent_id_req) >= 0);
+ vp->vp_bool = true;
+ break;
+
+ default:
+ rad_assert(0);
+ }
+
+ return 0;
+}
+
+/** Copy the incoming identity to the permanent identity attribute
+ *
+ * If the incoming ID really looks like a permanent ID, and we were
+ * told it was a permanent ID, then (optionally) trim the first byte
+ * to form the real permanent ID.
+ *
+ * Otherwise copy the entire incoming Identity to the
+ * &session-state:Permanent-Identity attribute.
+ *
+ * @param[in] request The current request.
+ * @param[in] in current identity.
+ * @param[in] eap_type The current eap_type.
+ * @param[in] strip_hint Whether to strip the hint byte off the permanent identity
+ */
+static int identity_to_permanent_identity(REQUEST *request, VALUE_PAIR *in, eap_type_t eap_type, bool strip_hint)
+{
+ fr_aka_sim_id_type_t our_type;
+ fr_aka_sim_method_hint_t our_method, expected_method;
+ VALUE_PAIR *vp;
+
+ if (in->vp_length == 0) {
+ RDEBUG2("Not processing zero length identity");
+ return -1;
+ }
+
+ /*
+ * Not requested to strip hint, don't do anything
+ * fancy, just copy Identity -> Permanent-Identity.
+ */
+ if (!strip_hint) {
+ MEM(fr_pair_update_by_da(request->state_ctx, &vp,
+ &request->state, attr_eap_aka_sim_permanent_identity) >= 0);
+ fr_pair_value_bstrncpy(vp, in->vp_strvalue, in->vp_length);
+ return 0;
+ }
+
+ switch (eap_type) {
+ case FR_EAP_METHOD_SIM:
+ expected_method = AKA_SIM_METHOD_HINT_SIM;
+ break;
+
+ case FR_EAP_METHOD_AKA:
+ expected_method = AKA_SIM_METHOD_HINT_AKA;
+ break;
+
+ case FR_EAP_METHOD_AKA_PRIME:
+ expected_method = AKA_SIM_METHOD_HINT_AKA_PRIME;
+ break;
+
+ default:
+ return -1;
+ }
+
+ /*
+ * First, lets see if this looks like an identity
+ * we can process.
+ *
+ * For now we allow all permanent identities no
+ * matter what EAP method.
+ *
+ * This is because we could be starting a different
+ * EAP method to the one the identity hinted,
+ * but we still want to strip the first byte.
+ */
+ if ((fr_aka_sim_id_type(&our_type, &our_method, in->vp_strvalue, in->vp_length) < 0) ||
+ (our_type != AKA_SIM_ID_TYPE_PERMANENT)) {
+ MEM(fr_pair_update_by_da(request->state_ctx, &vp,
+ &request->state, attr_eap_aka_sim_permanent_identity) >= 0);
+ fr_pair_value_bstrncpy(vp, in->vp_strvalue, in->vp_length);
+
+ RDEBUG2("%s has incorrect hint byte, expected '%c', got '%c'. "
+ "'hint' byte not stripped",
+ attr_eap_aka_sim_permanent_identity->name,
+ fr_aka_sim_hint_byte(AKA_SIM_ID_TYPE_PERMANENT, expected_method),
+ fr_aka_sim_hint_byte(our_type, our_method));
+ RINDENT();
+ RDEBUG2("&session-state:%pP", vp);
+ REXDENT();
+ } else {
+ /*
+ * To get here the identity must be >= 1 and must have
+ * had the expected hint byte.
+ *
+ * Strip off the hint byte, and then add the permanent
+ * identity to the output list.
+ */
+ MEM(fr_pair_update_by_da(request->state_ctx, &vp,
+ &request->state, attr_eap_aka_sim_permanent_identity) >= 0);
+ fr_pair_value_bstrncpy(vp, in->vp_strvalue + 1, in->vp_length - 1);
+
+ RDEBUG2("Stripping 'hint' byte from %s", attr_eap_aka_sim_permanent_identity->name);
+ RINDENT();
+ RDEBUG2("&session-state:%pP", vp);
+ REXDENT();
+ }
+
+ return 0;
+}
+
+/** Set the crypto identity from a received identity
+ *
+ */
+static void identity_to_crypto_identity(REQUEST *request, eap_aka_sim_session_t *eap_aka_sim_session,
+ uint8_t const *identity, size_t len)
+{
+ RDEBUG3("Setting cryptographic identity to \"%pV\"", fr_box_strvalue_len((char const *)identity, len));
+
+ talloc_free(eap_aka_sim_session->keys.identity);
+ eap_aka_sim_session->keys.identity_len = len;
+ MEM(eap_aka_sim_session->keys.identity = talloc_memdup(eap_aka_sim_session, identity, len));
+}
+
+/** Determine if we're after authentication
+ *
+ */
+static inline bool after_authentication(eap_aka_sim_session_t *eap_aka_sim_session)
+{
+ return eap_aka_sim_session->challenge_success || eap_aka_sim_session->reauthentication_success;
+}
+
+/** Resume after 'store session { ... }'
+ *
+ */
+static rlm_rcode_t session_store_resume(void *instance, UNUSED void *thread, REQUEST *request, void *rctx)
+{
+ aka_sim_state_enter_t state_enter = (aka_sim_state_enter_t)rctx;
+
+ switch (request->rcode) {
+ /*
+ * Store failed. Don't send fastauth id
+ */
+ case RLM_MODULE_USER_SECTION_REJECT:
+ pair_delete_reply(attr_eap_aka_sim_next_reauth_id);
+ break;
+
+ default:
+ break;
+ }
+
+ pair_delete_request(attr_eap_aka_sim_next_reauth_id);
+
+ return state_enter(instance, request, eap_session_get(request->parent));
+}
+
+/** Resume after 'store pseudonym { ... }'
+ *
+ */
+static rlm_rcode_t pseudonym_store_resume(void *instance, UNUSED void *thread, REQUEST *request, void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ aka_sim_state_enter_t state_enter = (aka_sim_state_enter_t)rctx;
+ VALUE_PAIR *vp;
+ VALUE_PAIR *new;
+
+ switch (request->rcode) {
+ /*
+ * Store failed. Don't send pseudonym
+ */
+ case RLM_MODULE_USER_SECTION_REJECT:
+ pair_delete_reply(attr_eap_aka_sim_next_pseudonym);
+ break;
+
+ default:
+ break;
+ }
+
+ request->rcode = RLM_MODULE_NOOP; /* Needed because we may call resume functions directly */
+
+ pair_delete_request(attr_eap_aka_sim_next_pseudonym);
+
+ vp = fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_next_reauth_id, TAG_ANY);
+ if (vp) {
+ /*
+ * Generate a random fastauth string
+ */
+ if (vp->vp_length == 0) {
+ char *identity;
+
+ if (!inst->ephemeral_id_length) {
+ RWDEBUG("Found empty Next-Reauth-Id, and told not to generate one. "
+ "Skipping store session { ... } section");
+
+ goto done;
+ }
+
+ MEM(identity = talloc_array(vp, char, inst->ephemeral_id_length + 2));
+ switch (eap_aka_sim_session->type) {
+ case FR_EAP_METHOD_SIM:
+ identity[0] = (char)ID_TAG_SIM_FASTAUTH;
+ break;
+
+ case FR_EAP_METHOD_AKA:
+ identity[0] = (char)ID_TAG_AKA_FASTAUTH;
+ break;
+
+ case FR_EAP_METHOD_AKA_PRIME:
+ identity[0] = (char)ID_TAG_AKA_PRIME_FASTAUTH;
+ break;
+
+ default:
+ break;
+ }
+ fr_rand_str((uint8_t *)identity + 1, inst->ephemeral_id_length, 'a');
+ identity[talloc_array_length(identity) - 1] = '\0';
+
+ fr_value_box_strdup_buffer_shallow(NULL, &vp->data, NULL, identity, false);
+ }
+ pair_update_request(&new, attr_session_id);
+ fr_pair_value_memcpy(new, (uint8_t const *)vp->vp_strvalue, vp->vp_length, vp->vp_tainted);
+
+ MEM(eap_aka_sim_session->fastauth_sent = talloc_bstrndup(eap_aka_sim_session,
+ vp->vp_strvalue, vp->vp_length));
+
+ switch (eap_aka_sim_session->type) {
+ /*
+ * AKA and SIM use the original MK for session resumption.
+ */
+ case FR_EAP_METHOD_SIM:
+ case FR_EAP_METHOD_AKA:
+ MEM(pair_update_session_state(&vp, attr_session_data) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.mk,
+ sizeof(eap_aka_sim_session->keys.mk), false);
+ break;
+ /*
+ * AKA' KDF 1 generates an additional key k_re
+ * which is used for reauthentication instead
+ * of the MK.
+ */
+ case FR_EAP_METHOD_AKA_PRIME:
+ MEM(pair_update_session_state(&vp, attr_session_data) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.k_re,
+ sizeof(eap_aka_sim_session->keys.k_re), false);
+ break;
+
+ default:
+ rad_assert(0);
+ break;
+ }
+
+ /*
+ * If the counter already exists in session
+ * state increment by 1, otherwise, add the
+ * attribute and set to zero.
+ */
+ vp = fr_pair_find_by_da(request->state, attr_eap_aka_sim_counter, TAG_ANY);
+ if (vp) {
+ vp->vp_uint16++;
+ /*
+ * Will get incremented by 1 in
+ * reauthentication_send, so when
+ * used, it'll be 1 (as per the standard).
+ */
+ } else {
+ MEM(pair_add_session_state(&vp, attr_eap_aka_sim_counter) >= 0);
+ vp->vp_uint16 = 0;
+ }
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.store_session,
+ RLM_MODULE_NOOP,
+ session_store_resume,
+ mod_signal,
+ rctx);
+ }
+
+done:
+ return state_enter(inst, request, eap_session_get(request->parent));
+}
+
+/** Implements a set of states for storing pseudonym and fastauth identities
+ *
+ * At the end of challenge or reauthentication rounds, the user may have specified
+ * a pseudonym and fastauth identity to return to the supplicant.
+ *
+ * Call the appropriate sections to persist those values.
+ *
+ * @param[in] inst of rlm_eap_aka.
+ * @param[in] request the current request.
+ * @param[in] state_enter state entry function for the
+ * state to transition to *after* the current
+ * state.
+ * @return RLM_MODULE_HANDLED.
+ */
+static rlm_rcode_t session_and_pseudonym_store(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session,
+ aka_sim_state_enter_t state_enter)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ VALUE_PAIR *vp;
+ VALUE_PAIR *new;
+
+ request->rcode = RLM_MODULE_NOOP; /* Needed because we may call resume functions directly */
+
+ vp = fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_next_pseudonym, TAG_ANY);
+ if (vp) {
+ /*
+ * Generate a random pseudonym string
+ */
+ if (vp->vp_length == 0) {
+ char *identity;
+
+ if (!inst->ephemeral_id_length) {
+ RWDEBUG("Found empty Pseudonym-Id, and told not to generate one. "
+ "Skipping store pseudonym { ... } section");
+
+ return pseudonym_store_resume(inst,
+ module_thread_by_data(inst),
+ request, (void *)state_enter);
+ }
+
+ MEM(identity = talloc_array(vp, char, inst->ephemeral_id_length + 2));
+ fr_rand_str((uint8_t *)identity + 1, inst->ephemeral_id_length, 'a');
+ switch (eap_aka_sim_session->type) {
+ case FR_EAP_METHOD_SIM:
+ identity[0] = (char)ID_TAG_SIM_PSEUDONYM;
+ break;
+
+ case FR_EAP_METHOD_AKA:
+ identity[0] = (char)ID_TAG_AKA_PSEUDONYM;
+ break;
+
+ case FR_EAP_METHOD_AKA_PRIME:
+ identity[0] = (char)ID_TAG_AKA_PRIME_PSEUDONYM;
+ break;
+
+ default:
+ break;
+ }
+ identity[talloc_array_length(identity) - 1] = '\0';
+ fr_value_box_strdup_buffer_shallow(NULL, &vp->data, NULL, identity, false);
+ }
+ pair_update_request(&new, attr_eap_aka_sim_next_pseudonym);
+ fr_pair_value_copy(new, vp);
+
+ MEM(eap_aka_sim_session->pseudonym_sent = talloc_bstrndup(eap_aka_sim_session,
+ vp->vp_strvalue, vp->vp_length));
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.store_pseudonym,
+ RLM_MODULE_NOOP,
+ pseudonym_store_resume,
+ mod_signal,
+ (void *)state_enter);
+ }
+
+ return pseudonym_store_resume(inst, module_thread_by_data(inst), request, (void *)state_enter);
+}
+
+/** Resume after 'clear session { ... }'
+ *
+ */
+static rlm_rcode_t session_clear_resume(void *instance, UNUSED void *thread, REQUEST *request, void *rctx)
+{
+ aka_sim_state_enter_t state_enter = (aka_sim_state_enter_t)rctx;
+
+ pair_delete_request(attr_session_id);
+
+ return state_enter(instance, request, eap_session_get(request->parent));
+}
+
+/** Resume after 'clear pseudonym { ... }'
+ *
+ */
+static rlm_rcode_t pseudonym_clear_resume(void *instance, UNUSED void *thread, REQUEST *request, void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ aka_sim_state_enter_t state_enter = (aka_sim_state_enter_t)rctx;
+
+ pair_delete_request(attr_eap_aka_sim_next_pseudonym);
+
+ /*
+ * Clear session
+ */
+ if (eap_aka_sim_session->fastauth_sent) {
+ VALUE_PAIR *vp;
+
+ pair_delete_request(attr_session_id);
+
+ MEM(pair_update_request(&vp, attr_session_id) >= 0);
+ fr_value_box_memcpy(vp, &vp->data, NULL,
+ (uint8_t *)eap_aka_sim_session->fastauth_sent,
+ talloc_array_length(eap_aka_sim_session->fastauth_sent) - 1, true);
+ TALLOC_FREE(eap_aka_sim_session->fastauth_sent);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.clear_session,
+ RLM_MODULE_NOOP,
+ session_clear_resume,
+ mod_signal,
+ rctx);
+ }
+
+ return state_enter(inst, request, eap_session_get(request->parent));
+}
+
+/** Implements a set of states for clearing out pseudonym and fastauth identities
+ *
+ * If either a Challenge round or Reauthentication round fail, we need to clear
+ * any identities that were provided during those rounds, as the supplicant
+ * will have discarded them.
+ *
+ * @param[in] inst of rlm_eap_aka.
+ * @param[in] request the current request.
+ * @param[in] state_enter state entry function for the
+ * state to transition to *after* the current
+ * state.
+ * @return RLM_MODULE_HANDLED.
+ */
+static rlm_rcode_t session_and_pseudonym_clear(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session,
+ aka_sim_state_enter_t state_enter)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ /*
+ * Clear out pseudonyms
+ */
+ if (eap_aka_sim_session->pseudonym_sent) {
+ VALUE_PAIR *vp;
+
+ MEM(pair_update_request(&vp, attr_eap_aka_sim_next_pseudonym) >= 0);
+ fr_value_box_strdup_buffer(vp, &vp->data, NULL, eap_aka_sim_session->pseudonym_sent, true);
+ TALLOC_FREE(eap_aka_sim_session->pseudonym_sent);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.clear_pseudonym,
+ RLM_MODULE_NOOP,
+ session_clear_resume,
+ mod_signal,
+ (void *)state_enter);
+ }
+
+ return pseudonym_clear_resume(inst, module_thread_by_data(inst), request, (void *)state_enter);
+}
+
+/** Encode EAP-SIM/AKA['] attributes
+ *
+ */
+static int common_encode(REQUEST *request, eap_session_t *eap_session, uint16_t subtype,
+ uint8_t const *hmac_extra, size_t hmac_extra_len)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ fr_cursor_t cursor;
+ fr_cursor_t to_encode;
+ VALUE_PAIR *head = NULL, *vp, *subtype_vp;
+ ssize_t ret;
+ fr_aka_sim_encode_ctx_t encoder_ctx = {
+ .root = fr_dict_root(dict_eap_aka_sim),
+ .keys = &eap_aka_sim_session->keys,
+
+ .iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ .iv_included = false,
+
+ .hmac_md = eap_aka_sim_session->mac_md,
+ .eap_packet = eap_session->this_round->request,
+ .hmac_extra = hmac_extra,
+ .hmac_extra_len = hmac_extra_len
+ };
+
+ /*
+ * Set the subtype to identity request
+ */
+ MEM(pair_update_reply(&subtype_vp, attr_eap_aka_sim_subtype) >= 0);
+ subtype_vp->vp_uint16 = subtype;
+
+ /*
+ * State what kind of request we're sending
+ */
+ if (RDEBUG_ENABLED2) {
+ switch (subtype) {
+ case FR_SUBTYPE_VALUE_SIM_START:
+ case FR_SUBTYPE_VALUE_AKA_IDENTITY:
+ RDEBUG2("Sending EAP-Request/%pV (%s)", &subtype_vp->data,
+ fr_table_str_by_value(fr_aka_sim_id_request_table, eap_aka_sim_session->id_req, "<INVALID>"));
+ break;
+
+ default:
+ RDEBUG2("Sending EAP-Request/%pV", &subtype_vp->data);
+ break;
+ }
+ }
+
+ fr_cursor_init(&cursor, &request->reply->vps);
+ fr_cursor_init(&to_encode, &head);
+
+ /*
+ * We have to be *SUPER* careful here not to reorder
+ * attributes, because for EAP-SIM the RAND values
+ * must be inserted into the packet in exactly the
+ * same order as they appear in fr_sim_keys_t
+ * otherwise the KDF will produce a different
+ * result.
+ */
+ while ((vp = fr_cursor_current(&cursor))) {
+ if (!fr_dict_parent_common(encoder_ctx.root, vp->da, true)) {
+ fr_cursor_next(&cursor);
+ continue;
+ }
+ vp = fr_cursor_remove(&cursor);
+
+ /*
+ * Silently discard encrypted attributes until
+ * the peer should have k_encr. These can be
+ * added by policy, and seem to cause
+ * wpa_supplicant to fail if sent before the challenge.
+ */
+ if (!eap_aka_sim_session->allow_encrypted &&
+ fr_dict_parent_common(attr_eap_aka_sim_encr_data, vp->da, true)) {
+ RWDEBUG("Silently discarding &reply:%s: Encrypted attributes not allowed in this round",
+ vp->da->name);
+ talloc_free(vp);
+ continue;
+ }
+
+ fr_cursor_prepend(&to_encode, vp);
+ }
+
+
+ RDEBUG2("Encoding attributes");
+ log_request_pair_list(L_DBG_LVL_2, request, head, NULL);
+
+ eap_session->this_round->request->type.num = eap_aka_sim_session->type;
+ eap_session->this_round->request->id = eap_aka_sim_session->id++ & 0xff;
+ eap_session->this_round->set_request_id = true;
+
+ ret = fr_aka_sim_encode(request, head, &encoder_ctx);
+ fr_cursor_head(&to_encode);
+ fr_cursor_free_list(&to_encode);
+
+ if (ret < 0) {
+ RPEDEBUG("Failed encoding attributes");
+ return -1;
+ }
+ return 0;
+}
+
+/** Send a EAP-Failure message to the supplicant
+ *
+ */
+static rlm_rcode_t common_eap_failure_send(REQUEST *request, eap_session_t *eap_session)
+{
+ RDEBUG2("Sending EAP-Failure");
+
+ eap_session->this_round->request->code = FR_EAP_CODE_FAILURE;
+ eap_session->finished = true;
+
+ return RLM_MODULE_REJECT;
+}
+
+/** Send a EAP-Request/(AKA|SIM)-Notification (Failure) message to the supplicant
+ *
+ */
+static rlm_rcode_t common_failure_notification_send(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ VALUE_PAIR *vp, *notification_vp;
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ /*
+ * Allow the user to specify specific failure notification
+ * types. We assume the user knows what they're doing and
+ * only toggle success and phase bits.
+ *
+ * This allows custom notification schemes to be implemented.
+ *
+ * If this is prior to authentication, valid values are:
+ * - FR_NOTIFICATION_VALUE_GENERAL_FAILURE
+ *
+ * If this is after authentication, valid values are:
+ * - FR_NOTIFICATION_VALUE_GENERAL_FAILURE_AFTER_AUTHENTICATION
+ * - FR_NOTIFICATION_VALUE_TEMPORARILY_DENIED - User has been
+ * temporarily denied access to the requested service.
+ * - FR_NOTIFICATION_VALUE_NOT_SUBSCRIBED
+ * User has not subscribed to the requested service.
+ */
+ notification_vp = fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_notification, TAG_ANY);
+
+ /*
+ * Change the failure notification depending where
+ * we are in the eap_aka_state machine.
+ */
+ if (after_authentication(eap_aka_sim_session)) {
+ if (!notification_vp) {
+ pair_add_reply(¬ification_vp, attr_eap_aka_sim_notification);
+ notification_vp->vp_uint16 = eap_aka_sim_session->failure_type; /* Default will be zero */
+ }
+
+ notification_vp->vp_uint16 &= ~0x4000; /* Unset phase bit */
+
+ /*
+ * Include the counter attribute if we're failing
+ * after a reauthentication success.
+ *
+ * RFC 4187 Section #9.10
+ *
+ * If EAP-Request/AKA-Notification is used on
+ * a fast re-authentication exchange, and if
+ * the P bit in AT_NOTIFICATION is set to zero,
+ * then AT_COUNTER is used for replay protection.
+ * In this case, the AT_ENCR_DATA and AT_IV
+ * attributes MUST be included, and the
+ * encapsulated plaintext attributes MUST include
+ * the AT_COUNTER attribute. The counter value
+ * included in AT_COUNTER MUST be the same
+ * as in the EAP-Request/AKA-Reauthentication
+ * packet on the same fast re-authentication
+ * exchange.
+ *
+ * If the counter is used it should never be zero,
+ * as it's incremented on first reauthentication
+ * request.
+ */
+ if (eap_aka_sim_session->reauthentication_success) {
+ MEM(pair_update_reply(¬ification_vp, attr_eap_aka_sim_counter) >= 0);
+ notification_vp->vp_uint16 = eap_aka_sim_session->keys.reauth.counter;
+ }
+
+ /*
+ * If we're after the challenge phase
+ * then we need to include a MAC to
+ * protect notifications.
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_mac) >= 0);
+ fr_pair_value_memcpy(vp, NULL, 0, false);
+ } else {
+ /*
+ * Only valid code is general failure
+ */
+ if (!notification_vp) {
+ pair_add_reply(¬ification_vp, attr_eap_aka_sim_notification);
+ notification_vp->vp_uint16 = FR_NOTIFICATION_VALUE_GENERAL_FAILURE;
+ /*
+ * User supplied failure code
+ */
+ } else {
+ notification_vp->vp_uint16 |= 0x4000; /* Set phase bit */
+ }
+ }
+ notification_vp->vp_uint16 &= ~0x8000; /* In both cases success bit should be low */
+
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
+ /*
+ * Encode the packet
+ */
+ if (common_encode(request, eap_session, FR_SUBTYPE_VALUE_AKA_SIM_NOTIFICATION, NULL, 0) < 0) {
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ return RLM_MODULE_HANDLED;
+}
+
+/** Add MPPE keys to the request being sent to the supplicant
+ *
+ * The only work to be done is the add the appropriate SEND/RECV
+ * attributes derived from the MSK.
+ */
+static rlm_rcode_t common_eap_success_send(REQUEST *request, eap_session_t *eap_session)
+{
+ uint8_t *p;
+ eap_aka_sim_session_t *eap_aka_sim_session;
+
+ RDEBUG2("Sending EAP-Success");
+
+ eap_session->this_round->request->code = FR_EAP_CODE_SUCCESS;
+ eap_session->finished = true;
+
+ eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque, eap_aka_sim_session_t);
+
+ RDEBUG2("Adding attributes for MSK");
+ p = eap_aka_sim_session->keys.msk;
+ eap_add_reply(request->parent, attr_ms_mppe_recv_key, p, EAP_TLS_MPPE_KEY_LEN);
+ p += EAP_TLS_MPPE_KEY_LEN;
+ eap_add_reply(request->parent, attr_ms_mppe_send_key, p, EAP_TLS_MPPE_KEY_LEN);
+
+ return RLM_MODULE_OK;
+}
+
+/** Send a EAP-Request/(AKA|SIM)-Notification (Success) message to the supplicant
+ *
+ */
+static rlm_rcode_t common_success_notification_send(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque, eap_aka_sim_session_t);
+ VALUE_PAIR *vp;
+
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
+ if (!fr_cond_assert(after_authentication(eap_aka_sim_session))) return RLM_MODULE_FAIL;
+
+ /*
+ * If we're in this state success bit is
+ * high phase bit is low.
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_notification) >= 0);
+ vp->vp_uint16 = FR_NOTIFICATION_VALUE_SUCCESS;
+
+ /*
+ * RFC 4187 section #9.10
+ *
+ * If EAP-Request/AKA-Notification is used on
+ * a fast re-authentication exchange, and if
+ * the P bit in AT_NOTIFICATION is set to zero,
+ * then AT_COUNTER is used for replay protection.
+ * In this case, the AT_ENCR_DATA and AT_IV
+ * attributes MUST be included, and the
+ * encapsulated plaintext attributes MUST include
+ * the AT_COUNTER attribute. The counter value
+ * included in AT_COUNTER MUST be the same
+ * as in the EAP-Request/AKA-Reauthentication
+ * packet on the same fast re-authentication
+ * exchange.
+ *
+ * If the counter is used it should never be zero,
+ * as it's incremented on first reauthentication
+ * request.
+ */
+ if (eap_aka_sim_session->keys.reauth.counter > 0) {
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_counter) >= 0);
+ vp->vp_uint16 = eap_aka_sim_session->keys.reauth.counter;
+ }
+
+ /*
+ * Need to include an AT_MAC attribute so that
+ * it will get calculated.
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_mac) >= 0);
+ fr_pair_value_memcpy(vp, NULL, 0, false);
+
+ /*
+ * Encode the packet
+ */
+ if (common_encode(request, eap_session, FR_SUBTYPE_VALUE_AKA_SIM_NOTIFICATION, NULL, 0) < 0) {
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ return RLM_MODULE_HANDLED;
+}
+
+/** Called after 'store session { ... }' and 'store pseudonym { ... }'
+ *
+ */
+static rlm_rcode_t common_reauthentication_request_send(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ /*
+ * Encode the packet - AT_IV is handled automatically
+ * by the encoder.
+ */
+ if (common_encode(request, eap_session, FR_SUBTYPE_VALUE_AKA_SIM_REAUTHENTICATION, NULL, 0) < 0) {
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ return RLM_MODULE_HANDLED;
+}
+
+/** Send a EAP-Request/(AKA|SIM)-Reauthenticate message to the supplicant
+ *
+ */
+static rlm_rcode_t common_reauthentication_request_compose(eap_aka_sim_common_conf_t *inst, REQUEST *request,
+ eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ VALUE_PAIR *to_peer = request->reply->vps, *vp;
+
+ VALUE_PAIR *kdf_id;
+
+ /*
+ * Allow override of KDF Identity
+ *
+ * Because certain handset manufacturers don't
+ * implement RFC 4187 correctly and use the
+ * wrong identity as input the the PRF/KDF.
+ *
+ * Not seen any doing this for re-authentication
+ * but you never know...
+ */
+ kdf_id = fr_pair_find_by_da(request->control, attr_eap_aka_sim_kdf_identity, TAG_ANY);
+ if (kdf_id) {
+ identity_to_crypto_identity(request, eap_aka_sim_session,
+ (uint8_t const *)kdf_id->vp_strvalue, kdf_id->vp_length);
+ fr_pair_delete_by_da(&request->control, attr_eap_aka_sim_kdf_identity);
+ }
+
+ RDEBUG2("Generating new session keys");
+
+ switch (eap_aka_sim_session->type) {
+ /*
+ * The GSM and UMTS KDF_0 mutate their keys using
+ * and identical algorithm.
+ */
+ case FR_EAP_METHOD_SIM:
+ case FR_EAP_METHOD_AKA:
+ if (fr_aka_sim_vector_gsm_umts_kdf_0_reauth_from_attrs(request, request->state,
+ &eap_aka_sim_session->keys) != 0) {
+ request_new_id:
+ switch (eap_aka_sim_session->last_id_req) {
+ /*
+ * Got here processing EAP-Identity-Response
+ * If this is the *true* reauth ID, then
+ * there's no point in setting AKA_SIM_ANY_ID_REQ.
+ */
+ case AKA_SIM_NO_ID_REQ:
+ case AKA_SIM_ANY_ID_REQ:
+ RDEBUG2("Composing EAP-Request/Reauthentication failed. Clearing reply attributes and "
+ "requesting additional Identity");
+ fr_pair_list_free(&request->reply->vps);
+ eap_aka_sim_session->id_req = AKA_SIM_FULLAUTH_ID_REQ;
+ return common_identity_enter(inst, request, eap_session);
+
+ case AKA_SIM_FULLAUTH_ID_REQ:
+ case AKA_SIM_PERMANENT_ID_REQ:
+ REDEBUG("Last requested fullauth or permanent ID, "
+ "but received, or were told we received (by policy), "
+ "a fastauth ID. Cannot continue");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ }
+ if (fr_aka_sim_crypto_kdf_0_reauth(&eap_aka_sim_session->keys) < 0) goto request_new_id;
+ break;
+
+ case FR_EAP_METHOD_AKA_PRIME:
+ switch (eap_aka_sim_session->kdf) {
+ case FR_KDF_VALUE_PRIME_WITH_CK_PRIME_IK_PRIME:
+ if (fr_aka_sim_vector_umts_kdf_1_reauth_from_attrs(request, request->state,
+ &eap_aka_sim_session->keys) != 0) {
+ goto request_new_id;
+ }
+ if (fr_aka_sim_crypto_umts_kdf_1_reauth(&eap_aka_sim_session->keys) < 0) goto request_new_id;
+ break;
+
+ default:
+ rad_assert(0);
+ break;
+ }
+ break;
+
+ default:
+ rad_assert(0);
+ break;
+ }
+
+ if (RDEBUG_ENABLED3) fr_aka_sim_crypto_keys_log(request, &eap_aka_sim_session->keys);
+
+ /*
+ * Add AT_IV, AT_COUNTER, AT_NONCE_S, and AT_MAC to to reply
+ * The user may have added AT_NEXT_REAUTH_ID, in which case
+ * we'll have sent that too.
+ */
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
+ /*
+ * Indicate we'd like to use protected success messages
+ * with AT_RESULT_IND
+ *
+ * Use our default, but allow user override too.
+ */
+ vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_result_ind, TAG_ANY);
+ if (vp) eap_aka_sim_session->send_result_ind = vp->vp_bool;
+
+ /*
+ * RFC 5448 says AT_BIDDING is only sent in the challenge
+ * not in reauthentication, so don't add that here.
+ */
+
+ /*
+ * Add AT_NONCE_S
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_nonce_s) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.reauth.nonce_s,
+ sizeof(eap_aka_sim_session->keys.reauth.nonce_s), false);
+
+ /*
+ * Add AT_COUNTER
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_counter) >= 0);
+ vp->vp_uint16 = eap_aka_sim_session->keys.reauth.counter;
+
+ /*
+ * need to include an empty AT_MAC attribute so that
+ * the mac will get calculated.
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_mac) >= 0);
+ fr_pair_value_memcpy(vp, NULL, 0, false);
+
+ /*
+ * If there's no checkcode_md we're not doing
+ * checkcodes.
+ */
+ if (eap_aka_sim_session->checkcode_md) {
+ /*
+ * If we have checkcode data, send that to the peer
+ * in AT_CHECKCODE for validation.
+ */
+ if (eap_aka_sim_session->checkcode_state) {
+ ssize_t slen;
+
+ slen = fr_aka_sim_crypto_finalise_checkcode(eap_aka_sim_session->checkcode,
+ &eap_aka_sim_session->checkcode_state);
+ if (slen < 0) {
+ RPEDEBUG("Failed calculating checkcode");
+ goto failure;
+ }
+ eap_aka_sim_session->checkcode_len = slen;
+
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_checkcode) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->checkcode, slen, false);
+ /*
+ * If we don't have checkcode data, then we exchanged
+ * no identity packets, so checkcode is zero.
+ */
+ } else {
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_checkcode) >= 0);
+ fr_pair_value_memcpy(vp, NULL, 0, false);
+ eap_aka_sim_session->checkcode_len = 0;
+ }
+ }
+
+ /*
+ * We've sent the challenge so the peer should now be able
+ * to accept encrypted attributes.
+ */
+ eap_aka_sim_session->allow_encrypted = true;
+
+ return session_and_pseudonym_store(inst, request, eap_session, common_reauthentication_request_send);
+}
+
+/** Called after 'store session { ... }' and 'store pseudonym { ... }'
+ *
+ */
+static rlm_rcode_t aka_challenge_request_send(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ /*
+ * Encode the packet - AT_IV is handled automatically
+ * by the encoder.
+ */
+ if (common_encode(request, eap_session, FR_SUBTYPE_VALUE_AKA_CHALLENGE, NULL, 0) < 0) {
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ return RLM_MODULE_HANDLED;
+}
+
+/** Send a EAP-Request/AKA-Challenge message to the supplicant
+ *
+ */
+static rlm_rcode_t aka_challenge_request_compose(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque, eap_aka_sim_session_t);
+ VALUE_PAIR *to_peer = request->reply->vps, *vp;
+ fr_aka_sim_vector_src_t src = AKA_SIM_VECTOR_SRC_AUTO;
+
+ VALUE_PAIR *kdf_id;
+
+ /*
+ * Allow override of KDF Identity
+ *
+ * Because certain handset manufacturers don't
+ * implement RFC 4187 correctly and use the
+ * wrong identity as input the the PRF/KDF.
+ */
+ kdf_id = fr_pair_find_by_da(request->control, attr_eap_aka_sim_kdf_identity, TAG_ANY);
+ if (kdf_id) {
+ identity_to_crypto_identity(request, eap_aka_sim_session,
+ (uint8_t const *)kdf_id->vp_strvalue, kdf_id->vp_length);
+ fr_pair_delete_by_da(&request->control, attr_eap_aka_sim_kdf_identity);
+ }
+
+ RDEBUG2("Acquiring UMTS vector(s)");
+
+ if (eap_aka_sim_session->type == FR_EAP_METHOD_AKA_PRIME) {
+ /*
+ * Copy the network name the user specified for
+ * key derivation purposes.
+ */
+ vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_kdf_input, TAG_ANY);
+ if (vp) {
+ talloc_free(eap_aka_sim_session->keys.network);
+ eap_aka_sim_session->keys.network = talloc_memdup(eap_aka_sim_session,
+ (uint8_t const *)vp->vp_strvalue,
+ vp->vp_length);
+ eap_aka_sim_session->keys.network_len = vp->vp_length;
+ } else {
+ REDEBUG("No network name available, can't set AT_KDF_INPUT");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ /*
+ * We don't allow the user to specify
+ * the KDF currently.
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_kdf) >= 0);
+ vp->vp_uint16 = eap_aka_sim_session->kdf;
+ }
+
+ /*
+ * Get vectors from attribute or generate
+ * them using COMP128-* or Milenage.
+ */
+ if (fr_aka_sim_vector_umts_from_attrs(request, request->control, &eap_aka_sim_session->keys, &src) != 0) {
+ REDEBUG("Failed retrieving UMTS vectors");
+ goto failure;
+ }
+
+ /*
+ * Don't leave the AMF hanging around
+ */
+ if (eap_aka_sim_session->type == FR_EAP_METHOD_AKA_PRIME) pair_delete_control(attr_sim_amf);
+
+ /*
+ * All set, calculate keys!
+ */
+ switch (eap_aka_sim_session->type) {
+ default:
+ case FR_EAP_METHOD_SIM:
+ rad_assert(0); /* EAP-SIM has its own Challenge state */
+ break;
+
+ case FR_EAP_METHOD_AKA:
+ fr_aka_sim_crypto_umts_kdf_0(&eap_aka_sim_session->keys);
+ break;
+
+ case FR_EAP_METHOD_AKA_PRIME:
+ switch (eap_aka_sim_session->kdf) {
+ case FR_KDF_VALUE_PRIME_WITH_CK_PRIME_IK_PRIME:
+ fr_aka_sim_crypto_umts_kdf_1(&eap_aka_sim_session->keys);
+ break;
+
+ default:
+ rad_assert(0);
+ break;
+ }
+ }
+ if (RDEBUG_ENABLED3) fr_aka_sim_crypto_keys_log(request, &eap_aka_sim_session->keys);
+
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
+ /*
+ * Indicate we'd like to use protected success messages
+ * with AT_RESULT_IND
+ *
+ * Use our default, but allow user override too.
+ */
+ vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_result_ind, TAG_ANY);
+ if (vp) eap_aka_sim_session->send_result_ind = vp->vp_bool;
+
+ /*
+ * See if we're indicating we want EAP-AKA'
+ * If so include AT_BIDDING with the correct
+ * value.
+ */
+ vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_bidding, TAG_ANY);
+ if (vp) {
+ eap_aka_sim_session->send_at_bidding_prefer_prime =
+ (vp->vp_uint16 == FR_BIDDING_VALUE_PREFER_AKA_PRIME);
+ }
+
+ /*
+ * These attributes are only allowed with
+ * EAP-AKA', protect users from themselves.
+ */
+ if (eap_aka_sim_session->type == FR_EAP_METHOD_AKA) {
+ pair_delete_reply(attr_eap_aka_sim_kdf_input);
+ pair_delete_reply(attr_eap_aka_sim_kdf);
+ }
+
+ /*
+ * Okay, we got the challenge! Put it into an attribute.
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_rand) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.umts.vector.rand, AKA_SIM_VECTOR_UMTS_RAND_SIZE, false);
+
+ /*
+ * Send the AUTN value to the client, so it can authenticate
+ * whoever has knowledge of the Ki.
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_autn) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.umts.vector.autn, AKA_SIM_VECTOR_UMTS_AUTN_SIZE, false);
+
+ /*
+ * need to include an AT_MAC attribute so that it will get
+ * calculated.
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_mac) >= 0);
+ fr_pair_value_memcpy(vp, NULL, 0, false);
+
+ /*
+ * If we have checkcode data, send that to the peer
+ * in AT_CHECKCODE for validation.
+ */
+ if (eap_aka_sim_session->checkcode_state) {
+ ssize_t slen;
+
+ slen = fr_aka_sim_crypto_finalise_checkcode(eap_aka_sim_session->checkcode,
+ &eap_aka_sim_session->checkcode_state);
+ if (slen < 0) {
+ RPEDEBUG("Failed calculating checkcode");
+ goto failure;
+ }
+ eap_aka_sim_session->checkcode_len = slen;
+
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_checkcode) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->checkcode, slen, false);
+ /*
+ * If we don't have checkcode data, then we exchanged
+ * no identity packets, so AT_CHECKCODE is zero.
+ */
+ } else {
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_checkcode) >= 0);
+ fr_pair_value_memcpy(vp, NULL, 0, false);
+ eap_aka_sim_session->checkcode_len = 0;
+ }
+
+ /*
+ * We've sent the challenge so the peer should now be able
+ * to accept encrypted attributes.
+ */
+ eap_aka_sim_session->allow_encrypted = true;
+
+ return session_and_pseudonym_store(inst, request, eap_session, aka_challenge_request_send);
+}
+
+/** Called after 'store session { ... }' and 'store pseudonym { ... }'
+ *
+ */
+static rlm_rcode_t sim_challenge_request_send(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ /*
+ * Encode the packet - AT_IV is handled automatically
+ * by the encoder.
+ */
+ if (common_encode(request, eap_session, FR_SUBTYPE_VALUE_SIM_CHALLENGE,
+ eap_aka_sim_session->keys.gsm.nonce_mt, sizeof(eap_aka_sim_session->keys.gsm.nonce_mt)) < 0) {
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ return RLM_MODULE_HANDLED;
+}
+
+/** Send a EAP-Request/SIM-Challenge message to the supplicant
+ *
+ */
+static rlm_rcode_t sim_challenge_request_compose(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ VALUE_PAIR *to_peer = request->reply->vps, *vp;
+ fr_aka_sim_vector_src_t src = AKA_SIM_VECTOR_SRC_AUTO;
+
+ VALUE_PAIR *kdf_id;
+
+ /*
+ * Allow override of KDF Identity
+ *
+ * Because certain handset manufacturers don't
+ * implement RFC 4187 correctly and use the
+ * wrong identity as input the the PRF/KDF.
+ */
+ kdf_id = fr_pair_find_by_da(request->control, attr_eap_aka_sim_kdf_identity, TAG_ANY);
+ if (kdf_id) {
+ identity_to_crypto_identity(request, eap_aka_sim_session,
+ (uint8_t const *)kdf_id->vp_strvalue, kdf_id->vp_length);
+ fr_pair_delete_by_da(&request->control, attr_eap_aka_sim_kdf_identity);
+ }
+
+ RDEBUG2("Acquiring GSM vector(s)");
+ if ((fr_aka_sim_vector_gsm_from_attrs(request, request->control, 0,
+ &eap_aka_sim_session->keys, &src) != 0) ||
+ (fr_aka_sim_vector_gsm_from_attrs(request, request->control, 1,
+ &eap_aka_sim_session->keys, &src) != 0) ||
+ (fr_aka_sim_vector_gsm_from_attrs(request, request->control, 2,
+ &eap_aka_sim_session->keys, &src) != 0)) {
+ REDEBUG("Failed retrieving SIM vectors");
+ return RLM_MODULE_FAIL;
+ }
+
+ fr_aka_sim_crypto_gsm_kdf_0(&eap_aka_sim_session->keys);
+
+ if (RDEBUG_ENABLED3) fr_aka_sim_crypto_keys_log(request, &eap_aka_sim_session->keys);
+
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
+ /*
+ * Indicate we'd like to use protected success messages
+ * with AT_RESULT_IND
+ *
+ * Use our default, but allow user override too.
+ */
+ vp = fr_pair_find_by_da(to_peer, attr_eap_aka_sim_result_ind, TAG_ANY);
+ if (vp) eap_aka_sim_session->send_result_ind = vp->vp_bool;
+
+ /*
+ * Okay, we got the challenges! Put them into attributes.
+ */
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_rand) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.gsm.vector[0].rand, AKA_SIM_VECTOR_GSM_RAND_SIZE, false);
+
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_rand) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.gsm.vector[1].rand, AKA_SIM_VECTOR_GSM_RAND_SIZE, false);
+
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_rand) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.gsm.vector[2].rand, AKA_SIM_VECTOR_GSM_RAND_SIZE, false);
+
+ /*
+ * need to include an AT_MAC attribute so that it will get
+ * calculated.
+ */
+ MEM(pair_update_reply(&vp, attr_eap_aka_sim_mac) >= 0);
+ fr_pair_value_memcpy(vp, NULL, 0, false);
+
+ /*
+ * We've sent the challenge so the peer should now be able
+ * to accept encrypted attributes.
+ */
+ eap_aka_sim_session->allow_encrypted = true;
+
+ return session_and_pseudonym_store(inst, request, eap_session, sim_challenge_request_send);
+}
+
+/** Send a EAP-Request/AKA-Identity message to the supplicant
+ *
+ * There are three types of user identities that can be implemented
+ * - Permanent identities such as 0123456789098765@myoperator.com
+ * Permanent identities can be identified by the leading zero followed by
+ * by 15 digits (the IMSI number).
+ * - Ephemeral identities (pseudonyms). These are identities assigned for
+ * identity privacy so the user can't be tracked. These can identities
+ * can either be generated as per the 3GPP 'Security aspects of non-3GPP accesses'
+ * document section 14, where a set of up to 16 encryption keys are used
+ * to reversibly encrypt the IMSI. Alternatively the pseudonym can be completely
+ * randomised and stored in a datastore.
+ * - A fast resumption ID which resolves to data used for fast resumption.
+ *
+ * In order to perform full authentication the original IMSI is required for
+ * forwarding to the HLR. In the case where we can't match/decrypt the pseudonym,
+ * or can't perform fast resumption, we need to request the full identity from
+ * the supplicant.
+ *
+ * @param[in] request The current subrequest.
+ * @param[in] eap_session to continue.
+ * @return
+ * - RLM_MODULE_HANDLED on success.
+ * - anything else on failure.
+ */
+static rlm_rcode_t aka_identity_request_send(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
+ /*
+ * Update eap_aka_sim_session->id_req in case the the
+ * user set attributes in `send Identity-Request { ... }`
+ * Also removes all existing id_req attributes
+ * from the reply.
+ */
+ identity_req_set_by_user(request, eap_aka_sim_session);
+
+ /*
+ * Select the right type of identity request attribute
+ *
+ * Implement checks on identity request order described
+ * by RFC4187 section #4.1.5.
+ *
+ * The internal state machine should always handle this
+ * correctly, but the user may have other ideas...
+ */
+ if (identity_req_pairs_add(request, eap_aka_sim_session) < 0) {
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ eap_aka_sim_session->last_id_req = eap_aka_sim_session->id_req; /* Record what we last requested */
+
+ /*
+ * Encode the packet
+ */
+ if (common_encode(request, eap_session, FR_SUBTYPE_VALUE_AKA_IDENTITY, NULL, 0) < 0) goto failure;
+
+ /*
+ * Digest the packet contents, updating our checkcode.
+ */
+ if (eap_aka_sim_session->checkcode_md) {
+ if (!eap_aka_sim_session->checkcode_state &&
+ fr_aka_sim_crypto_init_checkcode(eap_aka_sim_session, &eap_aka_sim_session->checkcode_state,
+ eap_aka_sim_session->checkcode_md) < 0) {
+ RPEDEBUG("Failed initialising checkcode");
+ goto failure;
+ }
+ if (fr_aka_sim_crypto_update_checkcode(eap_aka_sim_session->checkcode_state,
+ eap_session->this_round->request) < 0) {
+ RPEDEBUG("Failed updating checkcode");
+ goto failure;
+ }
+ }
+
+ return RLM_MODULE_HANDLED;
+}
+
+/** Send a EAP-Request/SIM-Start message to the supplicant
+ *
+ * There are three types of user identities that can be implemented
+ * - Permanent identities such as 0123456789098765@myoperator.com
+ * Permanent identities can be identified by the leading zero followed by
+ * by 15 digits (the IMSI number).
+ * - Ephemeral identities (pseudonyms). These are identities assigned for
+ * identity privacy so the user can't be tracked. These can identities
+ * can either be generated as per the 3GPP 'Security aspects of non-3GPP accesses'
+ * document section 14, where a set of up to 16 encryption keys are used
+ * to reversibly encrypt the IMSI. Alternatively the pseudonym can be completely
+ * randomised and stored in a datastore.
+ * - A fast resumption ID which resolves to data used for fast resumption.
+ *
+ * In order to perform full authentication the original IMSI is required for
+ * forwarding to the HLR. In the case where we can't match/decrypt the pseudonym,
+ * or can't perform fast resumption, we need to request the full identity from
+ * the supplicant.
+ *
+ * @param[in] request The current subrequest.
+ * @param[in] eap_session to continue.
+ * @return
+ * - RLM_MODULE_HANDLED on success.
+ * - anything else on failure.
+ */
+static rlm_rcode_t sim_start_request_send(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ VALUE_PAIR *vp;
+ fr_cursor_t cursor;
+ uint8_t *p, *end;
+
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
+ p = eap_aka_sim_session->keys.gsm.version_list;
+ end = p + sizeof(eap_aka_sim_session->keys.gsm.version_list);
+ eap_aka_sim_session->keys.gsm.version_list_len = 0;
+
+ /*
+ * If the user provided no versions, then
+ * just add the default (1).
+ */
+ if (!(fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_version_list, TAG_ANY))) {
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_version_list) >= 0);
+ vp->vp_uint16 = EAP_SIM_VERSION;
+ }
+
+ /*
+ * Iterate over the the versions adding them
+ * to the version list we use for keying.
+ */
+ for (vp = fr_cursor_init(&cursor, &request->reply->vps); vp; vp = fr_cursor_next(&cursor)) {
+ if (vp->da != attr_eap_aka_sim_version_list) continue;
+
+ if ((end - p) < 2) break;
+
+ /*
+ * Store as big endian
+ */
+ *p++ = (vp->vp_uint16 & 0xff00) >> 8;
+ *p++ = (vp->vp_uint16 & 0x00ff);
+ eap_aka_sim_session->keys.gsm.version_list_len += sizeof(uint16_t);
+ }
+
+ /*
+ * Update eap_aka_sim_session->id_req in case the the
+ * user set attributes in `send Identity-Request { ... }`
+ * Also removes all existing id_req attributes
+ * from the reply.
+ */
+ identity_req_set_by_user(request, eap_aka_sim_session);
+
+ /*
+ * Select the right type of identity request attribute
+ *
+ * Implement checks on identity request order described
+ * by RFC4186 section #4.2.5.
+ *
+ * The internal state machine should always handle this
+ * correctly, but the user may have other ideas...
+ */
+ if (identity_req_pairs_add(request, eap_aka_sim_session) < 0) {
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ eap_aka_sim_session->last_id_req = eap_aka_sim_session->id_req; /* Record what we last requested */
+
+ /*
+ * Encode the packet
+ */
+ if (common_encode(request, eap_session, FR_SUBTYPE_VALUE_SIM_START, NULL, 0) < 0) goto failure;
+
+ return RLM_MODULE_HANDLED;
+}
+
+/** Print debugging information, and write new state to eap_session->process
+ *
+ */
+static inline void state_transition(REQUEST *request, eap_session_t *eap_session,
+ module_method_t new_state)
+{
+ module_method_t old_state = eap_session->process;
+
+ if (new_state != old_state) {
+ RDEBUG2("Changed state %s -> %s",
+ module_state_method_to_str(aka_sim_stable_table, old_state, "<unknown>"),
+ module_state_method_to_str(aka_sim_stable_table, new_state, "<unknown>"));
+ } else {
+ RDEBUG2("Reentering state %s",
+ module_state_method_to_str(aka_sim_stable_table, old_state, "<unknown>"));
+ }
+
+ eap_session->process = new_state;
+}
+
+/** Resume after 'send EAP-Failure { ... }'
+ *
+ */
+static rlm_rcode_t common_eap_failure_enter_resume(UNUSED void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_session_t *eap_session = eap_session_get(request->parent);
+
+ section_rcode_ignored(request);
+
+ return common_eap_failure_send(request, eap_session);
+}
+
+/** Enter EAP-FAILURE state
+ *
+ */
+static rlm_rcode_t common_eap_failure_enter(eap_aka_sim_common_conf_t *inst, REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque, eap_aka_sim_session_t);
+
+ /*
+ * Free anything we were going to send out...
+ */
+ fr_pair_list_free(&request->reply->vps);
+
+ /*
+ * If we're failing, then any identities
+ * we sent are now invalid.
+ */
+ if (eap_aka_sim_session->pseudonym_sent || eap_aka_sim_session->fastauth_sent) {
+ return session_and_pseudonym_clear(inst,
+ request, eap_session, common_eap_failure_enter); /* come back when we're done */
+ }
+
+ state_transition(request, eap_session, common_eap_failure);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.send_eap_failure,
+ RLM_MODULE_NOOP,
+ common_eap_failure_enter_resume,
+ mod_signal,
+ NULL);
+}
+
+/** Resume after 'send Failure-Notification { ... }'
+ *
+ * Ignores return code from send Failure-Notification { ... } section.
+ */
+static rlm_rcode_t common_failure_notification_enter_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+
+ section_rcode_ignored(request);
+
+ /*
+ * Free anything we were going to send out...
+ */
+ fr_pair_list_free(&request->reply->vps);
+
+ /*
+ * If there's an issue composing the failure
+ * message just send an EAP-Failure instead.
+ */
+ return common_failure_notification_send(inst, request, eap_session);
+}
+
+/** Enter the FAILURE-NOTIFICATION state
+ *
+ */
+static rlm_rcode_t common_failure_notification_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque, eap_aka_sim_session_t);
+
+ /*
+ * If we're failing, then any identities
+ * we sent are now invalid.
+ */
+ if (eap_aka_sim_session->pseudonym_sent || eap_aka_sim_session->fastauth_sent) {
+ return session_and_pseudonym_clear(inst, request, eap_session,
+ common_failure_notification_enter); /* come back when we're done */
+ }
+
+ /*
+ * We've already sent a failure notification
+ * Now we just fail as it means something
+ * went wrong processing the ACK or we got
+ * garbage from the supplicant.
+ */
+ if (eap_session->process == common_failure_notification) {
+ return common_eap_failure_enter(inst, request, eap_session);
+ }
+
+ /*
+ * Otherwise just transition as normal...
+ */
+ state_transition(request, eap_session, common_failure_notification);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.send_failure_notification,
+ RLM_MODULE_NOOP,
+ common_failure_notification_enter_resume,
+ mod_signal,
+ NULL);
+}
+
+/** Resume after 'send EAP-Success { ... }'
+ *
+ */
+static rlm_rcode_t common_eap_success_enter_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ /*
+ * If this is true we're entering this state
+ * after sending a AKA-Success-Notification
+ *
+ * Is seems like a really bad idea to allow the
+ * user to send a protected success to the
+ * supplicant and then force a failure using
+ * the send EAP-Success { ... } section.
+ */
+ if (eap_aka_sim_session->send_result_ind) {
+ switch (request->rcode) {
+ case RLM_MODULE_USER_SECTION_REJECT:
+ RWDEBUG("Ignoring rcode (%s) from send EAP-Success { ... } "
+ "as we already sent a Success-Notification",
+ fr_table_str_by_value(mod_rcode_table, request->rcode, "<invalid>"));
+ RWDEBUG("If you need to force a failure, return an error code from "
+ "send Success-Notification { ... }");
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * But... if we're not working with protected
+ * success indication, this is the only
+ * opportunity the user has to force a failure at
+ * the end of authentication.
+ */
+ } else {
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+ }
+
+ return common_eap_success_send(request, eap_session);
+}
+
+/** Enter EAP-SUCCESS state
+ *
+ */
+static rlm_rcode_t common_eap_success_enter(eap_aka_sim_common_conf_t *inst, REQUEST *request,
+ eap_session_t *eap_session)
+{
+ state_transition(request, eap_session, common_eap_success);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.send_eap_success,
+ RLM_MODULE_NOOP,
+ common_eap_success_enter_resume,
+ mod_signal,
+ NULL);
+}
+
+/** Resume after 'send Success-Notification { ... }'
+ *
+ */
+static rlm_rcode_t common_success_notification_enter_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return common_success_notification_send(inst, request, eap_session);
+}
+
+/** Enter the SUCCESS-NOTIFICATION state
+ *
+ */
+static rlm_rcode_t common_success_notification_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ state_transition(request, eap_session, common_success_notification);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.send_success_notification,
+ RLM_MODULE_NOOP,
+ common_success_notification_enter_resume,
+ mod_signal,
+ NULL);
+}
+
+/** Resume after 'send Reauthentication-Request { ... }'
+ *
+ */
+static rlm_rcode_t common_reauthentication_send_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ switch (request->rcode) {
+ /*
+ * Failed getting the values we need for resumption
+ * Request a different identity.
+ */
+ default:
+ switch (eap_aka_sim_session->last_id_req) {
+ /*
+ * Got here processing EAP-Identity-Response
+ * If this is the *true* reauth ID, then
+ * there's no point in setting AKA_SIM_ANY_ID_REQ.
+ */
+ case AKA_SIM_NO_ID_REQ:
+ case AKA_SIM_ANY_ID_REQ:
+ RDEBUG2("Previous section returned (%s), clearing reply attributes and "
+ "requesting additional identity",
+ fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>"));
+ fr_pair_list_free(&request->reply->vps);
+ eap_aka_sim_session->id_req = AKA_SIM_FULLAUTH_ID_REQ;
+
+ return common_identity_enter(inst, request, eap_session);
+
+ case AKA_SIM_FULLAUTH_ID_REQ:
+ case AKA_SIM_PERMANENT_ID_REQ:
+ REDEBUG("Last requested Full-Auth-Id or Permanent-Identity, "
+ "but received a Fast-Auth-Id. Cannot continue");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+
+ }
+
+ /*
+ * Policy rejected the user
+ */
+ case RLM_MODULE_REJECT:
+ case RLM_MODULE_USERLOCK:
+ goto failure;
+
+ /*
+ * Everything looks ok, send the EAP-Request/reauthentication message
+ * After storing any new pseudonyms or session information.
+ */
+ case RLM_MODULE_NOOP:
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ return common_reauthentication_request_compose(inst, request, eap_session);
+ }
+}
+
+/** Resume after 'load session { ... }'
+ *
+ */
+static rlm_rcode_t session_load_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ pair_delete_request(attr_session_id);
+
+ /*
+ * Control attributes required could have been specified
+ * in another section.
+ */
+ if (!inst->actions.load_session) goto reauthenticate;
+
+ switch (request->rcode) {
+ /*
+ * Failed getting the values we need for resumption
+ * Request a different identity.
+ */
+ default:
+ switch (eap_aka_sim_session->last_id_req) {
+ /*
+ * Got here processing EAP-Identity-Response
+ * If this is the *true* reauth ID, then
+ * there's no point in setting AKA_SIM_ANY_ID_REQ.
+ */
+ case AKA_SIM_NO_ID_REQ:
+ case AKA_SIM_ANY_ID_REQ:
+ RDEBUG2("Previous section returned (%s), clearing reply attributes and "
+ "requesting additional identity",
+ fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>"));
+ fr_pair_list_free(&request->reply->vps);
+ eap_aka_sim_session->id_req = AKA_SIM_FULLAUTH_ID_REQ;
+ return common_identity_enter(inst, request, eap_session);
+
+ case AKA_SIM_FULLAUTH_ID_REQ:
+ case AKA_SIM_PERMANENT_ID_REQ:
+ REDEBUG("Last requested Full-Auth-Id or Permanent-Identity, "
+ "but received a Fast-Auth-Id. Cannot continue");
+ return common_failure_notification_enter(inst, request, eap_session);
+
+ }
+
+ /*
+ * Policy rejected the user
+ */
+ case RLM_MODULE_REJECT:
+ case RLM_MODULE_USERLOCK:
+ return common_failure_notification_enter(inst, request, eap_session);
+
+ /*
+ * Everything OK
+ */
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ reauthenticate:
+ return unlang_module_yield_to_section(request,
+ inst->actions.send_reauthentication_request,
+ RLM_MODULE_NOOP,
+ common_reauthentication_send_resume,
+ mod_signal,
+ NULL);
+ }
+}
+
+/** Resume after 'load pseudonym { ... }'
+ *
+ */
+static rlm_rcode_t pseudonym_load_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ aka_sim_state_enter_t state_enter = (aka_sim_state_enter_t)rctx;
+
+ pair_delete_request(attr_eap_aka_sim_next_reauth_id);
+
+ /*
+ * Control attributes required could have been specified
+ * in another section.
+ */
+ if (!inst->actions.load_pseudonym) {
+ next_state:
+ return state_enter(inst, request, eap_session);
+ }
+
+ switch (request->rcode) {
+ /*
+ * Failed resolving the pseudonym
+ * request a different identity.
+ */
+ default:
+ switch (eap_aka_sim_session->last_id_req) {
+ case AKA_SIM_NO_ID_REQ:
+ case AKA_SIM_ANY_ID_REQ:
+ case AKA_SIM_FULLAUTH_ID_REQ:
+ RDEBUG2("Previous section returned (%s), clearing reply attributes and "
+ "requesting additional identity",
+ fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>"));
+ fr_pair_list_free(&request->reply->vps);
+ eap_aka_sim_session->id_req = AKA_SIM_FULLAUTH_ID_REQ;
+ return common_identity_enter(inst, request, eap_session);
+
+ case AKA_SIM_PERMANENT_ID_REQ:
+ REDEBUG("Last requested a Permanent-Identity, but received a Pseudonym. Cannot continue");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ /*
+ * Policy rejected the user
+ */
+ case RLM_MODULE_REJECT:
+ case RLM_MODULE_USERLOCK:
+ goto failure;
+
+ /*
+ * Everything OK
+ */
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ goto next_state;
+ }
+}
+
+/** Enter the REAUTHENTICATION state
+ *
+ */
+static rlm_rcode_t common_reauthentication_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ VALUE_PAIR *vp = NULL;
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ state_transition(request, eap_session, common_reauthentication);
+
+ /*
+ * Add the current identity as session_id
+ * to make it easier to load/store things from
+ * the cache module.
+ */
+ MEM(pair_update_request(&vp, attr_session_id) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.identity, eap_aka_sim_session->keys.identity_len, true);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.load_session,
+ RLM_MODULE_NOOP,
+ session_load_resume,
+ mod_signal,
+ NULL);
+}
+
+/** Resume after 'send Challenge-Request { ... }'
+ *
+ */
+static rlm_rcode_t aka_challenge_enter_resume(void *instance, UNUSED void *thread, REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return aka_challenge_request_compose(inst, request, eap_session);
+}
+
+/** Enter the AKA-CHALLENGE state
+ *
+ */
+static rlm_rcode_t aka_challenge_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque, eap_aka_sim_session_t);
+ VALUE_PAIR *vp;
+
+ /*
+ * If we've sent either of these identities it
+ * means we've come here form a Reauthentication-Request
+ * that failed.
+ */
+ if (eap_aka_sim_session->pseudonym_sent || eap_aka_sim_session->fastauth_sent) {
+ return session_and_pseudonym_clear(inst, request, eap_session, aka_challenge_enter); /* come back when we're done */
+ }
+
+ state_transition(request, eap_session, aka_challenge);
+
+ /*
+ * Set some default attributes, giving the user a
+ * chance to modify them.
+ */
+ switch (eap_session->type) {
+ case FR_EAP_METHOD_AKA_PRIME:
+ {
+ uint8_t amf_buff[2] = { 0x80, 0x00 }; /* Set the AMF separation bit high */
+
+ /*
+ * Toggle the AMF high bit to indicate we're doing AKA'
+ */
+ MEM(pair_update_control(&vp, attr_sim_amf) >= 0);
+ fr_pair_value_memcpy(vp, amf_buff, sizeof(amf_buff), false);
+
+ /*
+ * Use the default network name we have configured
+ * and send it to the peer.
+ */
+ if (inst->network_name &&
+ !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_kdf_input, TAG_ANY)) {
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_kdf_input) >= 0);
+ fr_pair_value_bstrncpy(vp, inst->network_name, talloc_array_length(inst->network_name) - 1);
+ }
+ }
+ break;
+
+ default:
+ /*
+ * Use the default bidding value we have configured
+ */
+ if (eap_aka_sim_session->send_at_bidding_prefer_prime &&
+ !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_bidding, TAG_ANY)) {
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_bidding) >= 0);
+ vp->vp_uint16 = FR_BIDDING_VALUE_PREFER_AKA_PRIME;
+ }
+ break;
+
+ }
+
+ /*
+ * Set the defaults for protected result indicator
+ */
+ if (eap_aka_sim_session->send_result_ind &&
+ !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_result_ind) >= 0);
+ vp->vp_bool = true;
+ }
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.send_challenge_request,
+ RLM_MODULE_NOOP,
+ aka_challenge_enter_resume,
+ mod_signal,
+ NULL);
+}
+
+/** Resume after 'send Challenge-Request { ... }'
+ *
+ */
+static rlm_rcode_t sim_challenge_enter_resume(void *instance, UNUSED void *thread, REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return sim_challenge_request_compose(inst, request, eap_session);
+}
+
+/** Enter the SIM-CHALLENGE state
+ *
+ */
+static rlm_rcode_t sim_challenge_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ VALUE_PAIR *vp;
+
+ /*
+ * If we've sent either of these identities it
+ * means we've come here form a Reauthentication-Request
+ * that failed.
+ */
+ if (eap_aka_sim_session->pseudonym_sent || eap_aka_sim_session->fastauth_sent) {
+ return session_and_pseudonym_clear(inst, request, eap_session, sim_challenge_enter); /* come back when we're done */
+ }
+
+ state_transition(request, eap_session, sim_challenge);
+
+ /*
+ * Set the defaults for protected result indicator
+ */
+ if (eap_aka_sim_session->send_result_ind &&
+ !fr_pair_find_by_da(request->reply->vps, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+ MEM(pair_add_reply(&vp, attr_eap_aka_sim_result_ind) >= 0);
+ vp->vp_bool = true;
+ }
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.send_challenge_request,
+ RLM_MODULE_NOOP,
+ sim_challenge_enter_resume,
+ mod_signal,
+ NULL);
+}
+
+/** Enter the SIM-CHALLENGE or AKA-CHALLENGE state
+ *
+ * Called by functions which are common to both the EAP-SIM and EAP-AKA state machines
+ * to enter the correct challenge state.
+ */
+static rlm_rcode_t common_challenge_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ switch (eap_session->type) {
+ case FR_EAP_METHOD_SIM:
+ return sim_challenge_enter(inst, request, eap_session);
+
+ case FR_EAP_METHOD_AKA:
+ case FR_EAP_METHOD_AKA_PRIME:
+ return aka_challenge_enter(inst, request, eap_session);
+
+ default:
+ rad_assert(0);
+ }
+}
+
+/** Resume after 'send Identity-Request { ... }'
+ *
+ */
+static rlm_rcode_t aka_identity_enter_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return aka_identity_request_send(inst, request, eap_session);
+}
+
+/** Enter the AKA-IDENTITY state
+ *
+ */
+static rlm_rcode_t aka_identity_enter(eap_aka_sim_common_conf_t *inst, REQUEST *request, eap_session_t *eap_session)
+{
+ state_transition(request, eap_session, aka_identity);
+
+ /*
+ * If we have an send_aka_identity_request section
+ * then run that, otherwise just run the normal
+ * identity request section.
+ */
+ return unlang_module_yield_to_section(request,
+ inst->actions.aka.send_aka_identity_request ?
+ inst->actions.aka.send_aka_identity_request:
+ inst->actions.send_identity_request,
+ RLM_MODULE_NOOP,
+ aka_identity_enter_resume,
+ mod_signal,
+ NULL);
+}
+
+/** Resume after 'send Start { ... }'
+ *
+ */
+static rlm_rcode_t sim_start_enter_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return sim_start_request_send(inst, request, eap_session);
+}
+
+/** Enter the SIM-START state
+ *
+ */
+static rlm_rcode_t sim_start_enter(eap_aka_sim_common_conf_t *inst, REQUEST *request, eap_session_t *eap_session)
+{
+ state_transition(request, eap_session, sim_start);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.sim.send_sim_start_request ?
+ inst->actions.sim.send_sim_start_request:
+ inst->actions.send_identity_request,
+ RLM_MODULE_NOOP,
+ sim_start_enter_resume,
+ mod_signal,
+ NULL);
+}
+
+/** Enter the SIM-START or AKA-IDENTITY state
+ *
+ * Called by functions which are common to both the EAP-SIM and EAP-AKA state machines
+ * to enter the correct Identity-Request state.
+ */
+static rlm_rcode_t common_identity_enter(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ switch (eap_session->type) {
+ case FR_EAP_METHOD_SIM:
+ return sim_start_enter(inst, request, eap_session);
+
+ case FR_EAP_METHOD_AKA:
+ case FR_EAP_METHOD_AKA_PRIME:
+ return aka_identity_enter(inst, request, eap_session);
+
+ default:
+ rad_assert(0);
+ }
+}
+
+/** Process a EAP-Response/(AKA|SIM)-Reauthentication message - The response to our EAP-Request/(AKA|SIM)-Reauthentication message
+ *
+ */
+static rlm_rcode_t common_reauthentication_response_process(eap_aka_sim_common_conf_t *inst, REQUEST *request,
+ eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ uint8_t calc_mac[AKA_SIM_MAC_DIGEST_SIZE];
+ ssize_t slen;
+ VALUE_PAIR *mac, *checkcode;
+ VALUE_PAIR *from_peer = request->packet->vps;
+
+ mac = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_mac, TAG_ANY);
+ if (!mac) {
+ REDEBUG("Missing AT_MAC attribute");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ if (mac->vp_length != AKA_SIM_MAC_DIGEST_SIZE) {
+ REDEBUG("MAC has incorrect length, expected %u bytes got %zu bytes",
+ AKA_SIM_MAC_DIGEST_SIZE, mac->vp_length);
+ goto failure;
+ }
+
+ slen = fr_aka_sim_crypto_sign_packet(calc_mac, eap_session->this_round->response, true,
+ eap_aka_sim_session->mac_md,
+ eap_aka_sim_session->keys.k_aut, eap_aka_sim_session->keys.k_aut_len,
+ eap_aka_sim_session->keys.reauth.nonce_s,
+ sizeof(eap_aka_sim_session->keys.reauth.nonce_s));
+ if (slen < 0) {
+ RPEDEBUG("Failed calculating MAC");
+ goto failure;
+ } else if (slen == 0) {
+ REDEBUG("Zero length AT_MAC attribute");
+ goto failure;
+ }
+
+ if (memcmp(mac->vp_octets, calc_mac, sizeof(calc_mac)) == 0) {
+ RDEBUG2("Received MAC matches calculated MAC");
+ } else {
+ REDEBUG("Received MAC does not match calculated MAC");
+ RHEXDUMP_INLINE2(mac->vp_octets, AKA_SIM_MAC_DIGEST_SIZE, "Received");
+ RHEXDUMP_INLINE2(calc_mac, AKA_SIM_MAC_DIGEST_SIZE, "Expected");
+ goto failure;
+ }
+
+ /*
+ * If the peer doesn't include a checkcode then that
+ * means they don't support it, and we can't validate
+ * their view of the identity packets.
+ */
+ checkcode = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_checkcode, TAG_ANY);
+ if (checkcode) {
+ if (checkcode->vp_length != eap_aka_sim_session->checkcode_len) {
+ REDEBUG("Received checkcode's length (%zu) does not match calculated checkcode's length (%zu)",
+ checkcode->vp_length, eap_aka_sim_session->checkcode_len);
+ goto failure;
+ }
+
+ if (memcmp(checkcode->vp_octets, eap_aka_sim_session->checkcode,
+ eap_aka_sim_session->checkcode_len) == 0) {
+ RDEBUG2("Received checkcode matches calculated checkcode");
+ } else {
+ REDEBUG("Received checkcode does not match calculated checkcode");
+ RHEXDUMP_INLINE2(checkcode->vp_octets, checkcode->vp_length, "Received");
+ RHEXDUMP_INLINE2(eap_aka_sim_session->checkcode,
+ eap_aka_sim_session->checkcode_len, "Expected");
+ goto failure;
+ }
+ /*
+ * Only print something if we calculated a checkcode
+ */
+ } else if (eap_aka_sim_session->checkcode_len > 0){
+ RDEBUG2("Peer didn't include AT_CHECKCODE, skipping checkcode validation");
+ }
+
+ /*
+ * Check to see if the supplicant sent
+ * AT_COUNTER_TOO_SMALL, if they did then we
+ * clear out reauth information and enter the
+ * challenge state.
+ */
+ if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_counter_too_small, TAG_ANY)) {
+ RWDEBUG("Peer sent AT_COUNTER_TOO_SMALL (indicating our AT_COUNTER value (%u) wasn't fresh)",
+ eap_aka_sim_session->keys.reauth.counter);
+
+ fr_aka_sim_vector_umts_reauth_clear(&eap_aka_sim_session->keys);
+ eap_aka_sim_session->allow_encrypted = false;
+
+ return aka_challenge_enter(inst, request, eap_session);
+ }
+
+ /*
+ * If the peer wants a Success notification, and
+ * we included AT_RESULT_IND then send a success
+ * notification, otherwise send a normal EAP-Success.
+ *
+ * RFC 4187 Section #6.2. Result Indications
+ */
+ if (eap_aka_sim_session->send_result_ind) {
+ if (!fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+ RDEBUG("We wanted to use protected result indications, but peer does not");
+ eap_aka_sim_session->send_result_ind = false;
+ } else {
+ return common_success_notification_enter(inst, request, eap_session);
+ }
+ } else if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+ RDEBUG("Peer wanted to use protected result indications, but we do not");
+ }
+
+ eap_aka_sim_session->reauthentication_success = true;
+
+ return common_eap_success_enter(inst, request, eap_session);
+}
+
+/** Process a EAP-Response/AKA-Challenge message - The response to our EAP-Request/AKA-Challenge message
+ *
+ * Verify that MAC, and RES match what we expect.
+ */
+static rlm_rcode_t aka_challenge_response_process(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ uint8_t calc_mac[AKA_SIM_MAC_DIGEST_SIZE];
+ ssize_t slen;
+ VALUE_PAIR *vp = NULL, *mac, *checkcode;
+ VALUE_PAIR *from_peer = request->packet->vps;
+
+ mac = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_mac, TAG_ANY);
+ if (!mac) {
+ REDEBUG("Missing AT_MAC attribute");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ if (mac->vp_length != AKA_SIM_MAC_DIGEST_SIZE) {
+ REDEBUG("MAC has incorrect length, expected %u bytes got %zu bytes",
+ AKA_SIM_MAC_DIGEST_SIZE, mac->vp_length);
+ goto failure;
+ }
+
+ slen = fr_aka_sim_crypto_sign_packet(calc_mac, eap_session->this_round->response, true,
+ eap_aka_sim_session->mac_md,
+ eap_aka_sim_session->keys.k_aut, eap_aka_sim_session->keys.k_aut_len,
+ NULL, 0);
+ if (slen < 0) {
+ RPEDEBUG("Failed calculating MAC");
+ goto failure;
+ } else if (slen == 0) {
+ REDEBUG("Zero length AT_MAC attribute");
+ goto failure;
+ }
+
+ if (memcmp(mac->vp_octets, calc_mac, sizeof(calc_mac)) == 0) {
+ RDEBUG2("Received MAC matches calculated MAC");
+ } else {
+ REDEBUG("Received MAC does not match calculated MAC");
+ RHEXDUMP_INLINE2(mac->vp_octets, AKA_SIM_MAC_DIGEST_SIZE, "Received");
+ RHEXDUMP_INLINE2(calc_mac, AKA_SIM_MAC_DIGEST_SIZE, "Expected");
+ goto failure;
+ }
+
+ /*
+ * If the peer doesn't include a checkcode then that
+ * means they don't support it, and we can't validate
+ * their view of the identity packets.
+ */
+ if (eap_aka_sim_session->checkcode_md) {
+ checkcode = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_checkcode, TAG_ANY);
+ if (checkcode) {
+ if (checkcode->vp_length != eap_aka_sim_session->checkcode_len) {
+ REDEBUG("Received checkcode's length (%zu) does not match "
+ "calculated checkcode's length (%zu)",
+ checkcode->vp_length, eap_aka_sim_session->checkcode_len);
+ goto failure;
+ }
+
+ if (memcmp(checkcode->vp_octets,
+ eap_aka_sim_session->checkcode, eap_aka_sim_session->checkcode_len) == 0) {
+ RDEBUG2("Received checkcode matches calculated checkcode");
+ } else {
+ REDEBUG("Received checkcode does not match calculated checkcode");
+ RHEXDUMP_INLINE2(checkcode->vp_octets, checkcode->vp_length, "Received");
+ RHEXDUMP_INLINE2(eap_aka_sim_session->checkcode,
+ eap_aka_sim_session->checkcode_len, "Expected");
+ goto failure;
+ }
+ /*
+ * Only print something if we calculated a checkcode
+ */
+ } else if (eap_aka_sim_session->checkcode_len > 0){
+ RDEBUG2("Peer didn't include AT_CHECKCODE, skipping checkcode validation");
+ }
+ }
+
+ vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_res, TAG_ANY);
+ if (!vp) {
+ REDEBUG("AT_RES missing from challenge response");
+ goto failure;
+ }
+
+ if (vp->vp_length != eap_aka_sim_session->keys.umts.vector.xres_len) {
+ REDEBUG("Received RES' length (%zu) does not match calculated XRES' length (%zu)",
+ vp->vp_length, eap_aka_sim_session->keys.umts.vector.xres_len);
+ goto failure;
+ }
+
+ if (memcmp(vp->vp_octets, eap_aka_sim_session->keys.umts.vector.xres, vp->vp_length)) {
+ REDEBUG("Received RES does not match calculated XRES");
+ RHEXDUMP_INLINE2(vp->vp_octets, vp->vp_length, "RES :");
+ RHEXDUMP_INLINE2(eap_aka_sim_session->keys.umts.vector.xres,
+ eap_aka_sim_session->keys.umts.vector.xres_len, "XRES :");
+ goto failure;
+ }
+
+ RDEBUG2("Received RES matches calculated XRES");
+
+ eap_aka_sim_session->challenge_success = true;
+
+ /*
+ * If the peer wants a Success notification, and
+ * we included AT_RESULT_IND then send a success
+ * notification, otherwise send a normal EAP-Success.
+ *
+ * RFC 4187 Section #6.2. Result Indications
+ */
+ if (eap_aka_sim_session->send_result_ind) {
+ if (!fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+ RDEBUG("We wanted to use protected result indications, but peer does not");
+ eap_aka_sim_session->send_result_ind = false;
+ } else {
+ return common_success_notification_enter(inst, request, eap_session);
+ }
+ } else if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+ RDEBUG("Peer wanted to use protected result indications, but we do not");
+ }
+
+ return common_eap_success_enter(inst, request, eap_session);
+}
+
+/** Process a EAP-Response/SIM-Challenge message - The response to our EAP-Request/SIM-Challenge message
+ *
+ * Verify that MAC, and RES match what we expect.
+ */
+static rlm_rcode_t sim_challenge_response_process(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ uint8_t sres_cat[AKA_SIM_VECTOR_GSM_SRES_SIZE * 3];
+ uint8_t *p = sres_cat;
+
+ uint8_t calc_mac[AKA_SIM_MAC_DIGEST_SIZE];
+ ssize_t slen;
+ VALUE_PAIR *mac;
+ VALUE_PAIR *from_peer = request->packet->vps;
+
+ memcpy(p, eap_aka_sim_session->keys.gsm.vector[0].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
+ p += AKA_SIM_VECTOR_GSM_SRES_SIZE;
+ memcpy(p, eap_aka_sim_session->keys.gsm.vector[1].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
+ p += AKA_SIM_VECTOR_GSM_SRES_SIZE;
+ memcpy(p, eap_aka_sim_session->keys.gsm.vector[2].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
+
+ mac = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_mac, TAG_ANY);
+ if (!mac) {
+ REDEBUG("Missing AT_MAC attribute");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ if (mac->vp_length != AKA_SIM_MAC_DIGEST_SIZE) {
+ REDEBUG("MAC has incorrect length, expected %u bytes got %zu bytes",
+ AKA_SIM_MAC_DIGEST_SIZE, mac->vp_length);
+ goto failure;
+ }
+
+ slen = fr_aka_sim_crypto_sign_packet(calc_mac, eap_session->this_round->response, true,
+ eap_aka_sim_session->mac_md,
+ eap_aka_sim_session->keys.k_aut, eap_aka_sim_session->keys.k_aut_len,
+ sres_cat, sizeof(sres_cat));
+ if (slen < 0) {
+ RPEDEBUG("Failed calculating MAC");
+ goto failure;
+ } else if (slen == 0) {
+ REDEBUG("Zero length AT_MAC attribute");
+ goto failure;
+ }
+
+ if (memcmp(mac->vp_octets, calc_mac, sizeof(calc_mac)) == 0) {
+ RDEBUG2("Received MAC matches calculated MAC");
+ } else {
+ REDEBUG("Received MAC does not match calculated MAC");
+ RHEXDUMP_INLINE2(mac->vp_octets, AKA_SIM_MAC_DIGEST_SIZE, "Received");
+ RHEXDUMP_INLINE2(calc_mac, AKA_SIM_MAC_DIGEST_SIZE, "Expected");
+ goto failure;
+ }
+
+ eap_aka_sim_session->challenge_success = true;
+
+ /*
+ * If the peer wants a Success notification, and
+ * we included AT_RESULT_IND then send a success
+ * notification, otherwise send a normal EAP-Success.
+ */
+ if (eap_aka_sim_session->send_result_ind) {
+ if (!fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+ RDEBUG("We wanted to use protected result indications, but peer does not");
+ eap_aka_sim_session->send_result_ind = false;
+ } else {
+ return common_success_notification_enter(inst, request, eap_session);
+ }
+ } else if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_result_ind, TAG_ANY)) {
+ RDEBUG("Peer wanted to use protected result indications, but we do not");
+ }
+
+ return common_eap_success_enter(inst, request, eap_session);
+}
+
+/** Process a EAP-Response/AKA-Identity message i.e. an EAP-AKA Identity-Response
+ *
+ * - If the message does not contain AT_IDENTITY, then enter the FAILURE-NOTIFICATION state.
+ * - If the user requested another identity, re-enter the AKA-Identity sate.
+ * - ...or continue based on the value of &Identity-Type which was added by #aka_identity,
+ * and possibly modified by the user.
+ * - Fastauth - Enter the REAUTHENTICATION state.
+ * - Pseudonym - Call 'load pseudonym { ... }'
+ * - Permanent - Enter the CHALLENGE state.
+ */
+static rlm_rcode_t aka_identity_response_process(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ bool user_set_id_req;
+ VALUE_PAIR *identity_type;
+ VALUE_PAIR *from_peer = request->packet->vps;
+ /*
+ * Digest the identity response
+ */
+ if (eap_aka_sim_session->checkcode_md) {
+ if (fr_aka_sim_crypto_update_checkcode(eap_aka_sim_session->checkcode_state,
+ eap_session->this_round->response) < 0) {
+ RPEDEBUG("Failed updating checkcode");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ }
+
+ /*
+ * See if the user wants us to request another
+ * identity.
+ *
+ * If they set one themselves don't override
+ * what they set.
+ */
+ user_set_id_req = identity_req_set_by_user(request, eap_aka_sim_session);
+ if ((request->rcode == RLM_MODULE_NOTFOUND) || user_set_id_req) {
+ if (!user_set_id_req) {
+ switch (eap_aka_sim_session->last_id_req) {
+ case AKA_SIM_ANY_ID_REQ:
+ eap_aka_sim_session->id_req = AKA_SIM_FULLAUTH_ID_REQ;
+ break;
+
+ case AKA_SIM_FULLAUTH_ID_REQ:
+ eap_aka_sim_session->id_req = AKA_SIM_PERMANENT_ID_REQ;
+ break;
+
+ case AKA_SIM_NO_ID_REQ: /* Should not happen */
+ rad_assert(0);
+ /* FALL-THROUGH */
+
+ case AKA_SIM_PERMANENT_ID_REQ:
+ REDEBUG("Peer sent no usable identities");
+ goto failure;
+
+ }
+ RDEBUG2("Previous section returned (%s), requesting next most permissive identity (%s)",
+ fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>"),
+ fr_table_str_by_value(fr_aka_sim_id_request_table, eap_aka_sim_session->id_req, "<INVALID>"));
+ }
+ return aka_identity_enter(inst, request, eap_session);
+ }
+
+ /*
+ * If the identity looks like a fast re-auth id
+ * run fast re-auth, otherwise do fullauth.
+ */
+ identity_type = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity_type, TAG_ANY);
+ if (identity_type) switch (identity_type->vp_uint32) {
+ case FR_IDENTITY_TYPE_VALUE_FASTAUTH:
+ return common_reauthentication_enter(inst, request, eap_session);
+
+ /*
+ * It's a pseudonym, which now needs resolving.
+ * The resume function here calls aka_challenge_enter
+ * if pseudonym resolution went ok.
+ */
+ case FR_IDENTITY_TYPE_VALUE_PSEUDONYM:
+ return unlang_module_yield_to_section(request,
+ inst->actions.load_pseudonym,
+ RLM_MODULE_NOOP,
+ pseudonym_load_resume,
+ mod_signal,
+ (void *)aka_challenge_enter);
+ default:
+ break;
+ }
+
+ return aka_challenge_enter(inst, request, eap_session);
+}
+
+/** Helper function to check for the presence and length of AT_SELECTED_VERSION and copy its value into the keys structure
+ *
+ * Also checks the version matches one of the ones we advertised in our version list,
+ * which is a bit redundant seeing as there's only one version of EAP-SIM.
+ */
+static int sim_start_selected_version_check(REQUEST *request, VALUE_PAIR *from_peer,
+ eap_aka_sim_session_t *eap_aka_sim_session)
+{
+ VALUE_PAIR *selected_version_vp;
+
+ /*
+ * Check that we got an AT_SELECTED_VERSION
+ */
+ selected_version_vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_selected_version, TAG_ANY);
+ if (!selected_version_vp) {
+ REDEBUG("EAP-Response/SIM/Start does not contain AT_SELECTED_VERSION");
+ return -1;
+ }
+
+ /*
+ * See if the selected version was in our list
+ */
+ {
+ uint8_t selected_version[2];
+ uint8_t *p, *end;
+ bool found = false;
+
+ p = eap_aka_sim_session->keys.gsm.version_list;
+ end = p + eap_aka_sim_session->keys.gsm.version_list_len;
+
+ selected_version[0] = (selected_version_vp->vp_uint16 & 0xff00) >> 8;
+ selected_version[1] = (selected_version_vp->vp_uint16 & 0x00ff);
+
+ while (p < end) {
+ if ((p[0] == selected_version[0]) && (p[1] == selected_version[1])) {
+ found = true;
+ /*
+ * Update our keying material
+ */
+ eap_aka_sim_session->keys.gsm.version_select[0] = selected_version[0];
+ eap_aka_sim_session->keys.gsm.version_select[1] = selected_version[1];
+ break;
+ }
+ }
+
+ if (!found) {
+ REDEBUG("AT_SELECTED_VERSION (%u) does not match a value in our version list",
+ selected_version_vp->vp_uint16);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/** Helper function to check for the presence and length of AT_NONCE_MT and copy its value into the keys structure
+ *
+ * Does not actually perform cryptographic validation of AT_NONCE_MT, this is done later.
+ */
+static int sim_start_nonce_mt_check(REQUEST *request, VALUE_PAIR *from_peer,
+ eap_aka_sim_session_t *eap_aka_sim_session)
+{
+ VALUE_PAIR *nonce_mt_vp;
+
+ /*
+ * Copy nonce_mt to the keying material
+ */
+ nonce_mt_vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_nonce_mt, TAG_ANY);
+ if (!nonce_mt_vp) {
+ REDEBUG("EAP-Response/SIM/Start does not contain AT_NONCE_MT");
+ return -1;
+ }
+
+ if (nonce_mt_vp->vp_length != sizeof(eap_aka_sim_session->keys.gsm.nonce_mt)) {
+ REDEBUG("AT_NONCE_MT must be exactly %zu bytes, not %zu bytes",
+ sizeof(eap_aka_sim_session->keys.gsm.nonce_mt), nonce_mt_vp->vp_length);
+ return -1;
+ }
+ memcpy(eap_aka_sim_session->keys.gsm.nonce_mt, nonce_mt_vp->vp_octets,
+ sizeof(eap_aka_sim_session->keys.gsm.nonce_mt));
+
+ return 0;
+}
+
+/** Process a EAP-Response/SIM-Start message i.e. an EAP-SIM Identity-Response
+ *
+ * - If the message does not contain AT_IDENTITY, then enter the FAILURE-NOTIFICATION state.
+ * - If the user requested another identity, re-enter the SIM-START sate.
+ * - ...or continue based on the value of &Identity-Type which was added by #sim_start,
+ * and possibly modified by the user.
+ * - Fastauth
+ * - If AT_NONCE_MT or AT_SELECTED_VERSION are present, enter the FAILURE-NOTIFICATION state.
+ * - ...or enter the REAUTHENTICATION state.
+ * - Pseudonym - Verify selected version and AT_NONCE_MT, then call 'load pseudonym { ... }'
+ * - Permanent - Verify selected version and AT_NONCE_MT, then enter the CHALLENGE state.
+ */
+static rlm_rcode_t sim_start_response_process(eap_aka_sim_common_conf_t *inst,
+ REQUEST *request, eap_session_t *eap_session)
+{
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ bool user_set_id_req;
+ VALUE_PAIR *identity_type;
+
+ VALUE_PAIR *from_peer = request->packet->vps;
+
+ /*
+ * See if the user wants us to request another
+ * identity.
+ *
+ * If they set one themselves don't override
+ * what they set.
+ */
+ user_set_id_req = identity_req_set_by_user(request, eap_aka_sim_session);
+ if ((request->rcode == RLM_MODULE_NOTFOUND) || user_set_id_req) {
+ if (!user_set_id_req) {
+ switch (eap_aka_sim_session->last_id_req) {
+ case AKA_SIM_ANY_ID_REQ:
+ eap_aka_sim_session->id_req = AKA_SIM_FULLAUTH_ID_REQ;
+ break;
+
+ case AKA_SIM_FULLAUTH_ID_REQ:
+ eap_aka_sim_session->id_req = AKA_SIM_PERMANENT_ID_REQ;
+ break;
+
+ case AKA_SIM_NO_ID_REQ: /* Should not happen */
+ rad_assert(0);
+ /* FALL-THROUGH */
+
+ case AKA_SIM_PERMANENT_ID_REQ:
+ REDEBUG("Peer sent no usable identities");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ RDEBUG2("Previous section returned (%s), requesting next most permissive identity (%s)",
+ fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>"),
+ fr_table_str_by_value(fr_aka_sim_id_request_table, eap_aka_sim_session->id_req, "<INVALID>"));
+ }
+ return sim_start_enter(inst, request, eap_session);
+ }
+
+ /*
+ * If the identity looks like a fast re-auth id
+ * run fast re-auth, otherwise do fullauth.
+ */
+ identity_type = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity_type, TAG_ANY);
+ if (identity_type) switch (identity_type->vp_uint32) {
+ case FR_IDENTITY_TYPE_VALUE_FASTAUTH:
+ /*
+ * RFC 4186 Section #9.2
+ *
+ * The AT_NONCE_MT attribute MUST NOT be included if the AT_IDENTITY
+ * with a fast re-authentication identity is present for fast
+ * re-authentication
+ */
+ if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_nonce_mt, TAG_ANY)) {
+ REDEBUG("AT_NONCE_MT is not allowed in EAP-Response/SIM-Reauthentication messages");
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ /*
+ * RFC 4186 Section #9.2
+ *
+ * The AT_SELECTED_VERSION attribute MUST NOT be included if the
+ * AT_IDENTITY attribute with a fast re-authentication identity is
+ * present for fast re-authentication.
+ */
+ if (fr_pair_find_by_da(from_peer, attr_eap_aka_sim_selected_version, TAG_ANY)) {
+ REDEBUG("AT_SELECTED_VERSION is not allowed in EAP-Response/SIM-Reauthentication messages");
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ return common_reauthentication_enter(inst, request, eap_session);
+
+ /*
+ * It's a pseudonym, which now needs resolving.
+ * The resume function here calls aka_challenge_enter
+ * if pseudonym resolution went ok.
+ */
+ case FR_IDENTITY_TYPE_VALUE_PSEUDONYM:
+ if (sim_start_selected_version_check(request, from_peer, eap_aka_sim_session) < 0) goto failure;
+ if (sim_start_nonce_mt_check(request, from_peer, eap_aka_sim_session) < 0) goto failure;
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.load_pseudonym,
+ RLM_MODULE_NOOP,
+ pseudonym_load_resume,
+ mod_signal,
+ (void *)sim_challenge_enter);
+
+ /*
+ * If it's a permanent ID, copy it over to
+ * the session state list for use in the
+ * store pseudonym/store session sections
+ * later.
+ */
+ case FR_IDENTITY_TYPE_VALUE_PERMANENT:
+ if (sim_start_selected_version_check(request, from_peer, eap_aka_sim_session) < 0) goto failure;
+ if (sim_start_nonce_mt_check(request, from_peer, eap_aka_sim_session) < 0) goto failure;
+
+ /* FALL-THROUGH */
+ default:
+ break;
+ }
+
+ return sim_challenge_enter(inst, request, eap_session);
+}
+
+/** Resume after 'recv Failure-Notification-Ack { ... }'
+ *
+ * - Enter the EAP-FAILURE state.
+ */
+static rlm_rcode_t common_failure_notification_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+
+ section_rcode_ignored(request);
+
+ /*
+ * Case 2 where we're allowed to send an EAP-Failure
+ */
+ return common_eap_failure_enter(inst, request, eap_session);
+}
+
+/** Resume after 'recv Success-Notification-Ack { ... }'
+ *
+ * - Enter the EAP-SUCCESS state.
+ */
+static rlm_rcode_t common_success_notification_ack_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+
+ section_rcode_ignored(request);
+
+ /*
+ * RFC 4187 says we ignore the contents of the
+ * next packet after we send our success notification
+ * and always send a success.
+ */
+ return common_eap_success_enter(inst, request, eap_session);
+}
+
+/** Resume after 'recv Challenge-Response { ... }'
+ *
+ * - If the previous section returned a failure rcode, enter the FAILURE-NOTIFICATION state.
+ * - ...or call a function to process the contents of the AKA-Challenge message.
+ */
+static rlm_rcode_t aka_challenge_response_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return aka_challenge_response_process(inst, request, eap_session);
+}
+
+/** Resume after 'recv Challenge-Response { ... }'
+ *
+ * - If the previous section returned a failure rcode, enter the FAILURE-NOTIFICATION state.
+ * - ...or call a function to process the contents of the SIM-Challenge message.
+ */
+static rlm_rcode_t sim_challenge_response_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return sim_challenge_response_process(inst, request, eap_session);
+}
+
+/** Resume after 'recv Identity-Response { ... }' or 'recv AKA-Identity { ... }'
+ *
+ * - If the previous section returned a failure rcode, enter the FAILURE-NOTIFICATION state.
+ * - ...or call a function to process the contents of the AKA-Identity message, mainly the AT_IDENTITY value.
+ */
+static rlm_rcode_t aka_identity_response_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return aka_identity_response_process(inst, request, eap_session);
+}
+
+/** Resume after 'recv Identity-Response { ... }' or 'recv SIM-Start { ... }'
+ *
+ * - If the previous section returned a failure rcode, enter the FAILURE-NOTIFICATION state.
+ * - ...or call a function to process the contents of the SIM-Start message, mainly the AT_IDENTITY value.
+ */
+static rlm_rcode_t sim_start_response_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return sim_start_response_process(inst, request, eap_session);
+}
+
+/** Resume after 'recv Authentication-Reject { ... }'
+ *
+ * - Enter the FAILURE-NOTIFICATION state.
+ */
+static rlm_rcode_t aka_authentication_reject_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+
+ section_rcode_ignored(request);
+
+ /*
+ * Case 2 where we're allowed to send an EAP-Failure
+ */
+ return common_eap_failure_enter(inst, request, eap_session);
+}
+
+/** Resume after 'recv Synchronization-Failure { ... }'
+ *
+ * - If 'recv Synchronization-Failure { ... }' returned a failure
+ * rcode, enter the FAILURE-NOTIFICATION state.
+ * - ...or if no 'recv Syncronization-Failure { ... }' section was
+ * defined, then enter the FAILURE-NOTIFICATION state.
+ * - ...or if the user didn't provide a new SQN value in &control:SQN
+ * then enter the FAILURE-NOTIFICATION state.
+ * - ...or enter the AKA-CHALLENGE state.
+ */
+static rlm_rcode_t aka_synchronization_failure_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ VALUE_PAIR *vp;
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ /*
+ * If there's no section to handle this, then no resynchronisation
+ * can't have occurred and we just send a reject.
+ *
+ * Similarly, if we've already received one synchronisation failure
+ * then it's highly likely whatever user configured action was
+ * configured was unsuccessful, and we should just give up.
+ */
+ if (!inst->actions.aka.recv_syncronization_failure || eap_aka_sim_session->prev_recv_sync_failure) {
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ /*
+ * We couldn't generate an SQN and the user didn't provide one,
+ * so we need to fail.
+ */
+ vp = fr_pair_find_by_da(request->control, attr_sim_sqn, TAG_ANY);
+ if (!vp) {
+ REDEBUG("No &control:SQN value provided after resynchronisation, cannot continue");
+ goto failure;
+ }
+
+ /*
+ * RFC 4187 Section #6.3.1
+ *
+ * "if the peer detects that the
+ * sequence number in AUTN is not correct, the peer responds with
+ * EAP-Response/AKA-Synchronization-Failure (Section 9.6), and the
+ * server proceeds with a new EAP-Request/AKA-Challenge."
+ */
+ return aka_challenge_enter(inst, request, eap_session);
+}
+
+/** Resume after 'recv Client-Error { ... }'
+ *
+ * - Enter the EAP-FAILURE state.
+ */
+static rlm_rcode_t common_client_error_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+
+ section_rcode_ignored(request);
+
+ return common_eap_failure_enter(inst, request, eap_session);
+}
+
+/** Resume after 'recv Reauthentication-Response { ... }'
+ *
+ * - If 'recv Reauthentication-Response { ... }' returned a failure
+ * rcode, enter the FAILURE-NOTIFICATION state.
+ * - ...or call the EAP-Request/Reauthentication-Response function to act on the
+ * contents of the response.
+ */
+static rlm_rcode_t common_reauthentication_response_recv_resume(void *instance, UNUSED void *thread,
+ REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ return common_reauthentication_response_process(inst, request, eap_session);
+}
+
+/** Decode the peer's response
+ *
+ * This is called by the state_* functions to decode the peer's response.
+ */
+static rlm_rcode_t common_decode(VALUE_PAIR **subtype_vp, VALUE_PAIR **vps,
+ eap_aka_sim_common_conf_t *inst, REQUEST *request)
+{
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ fr_aka_sim_decode_ctx_t ctx = {
+ .keys = &eap_aka_sim_session->keys,
+ };
+ VALUE_PAIR *aka_vps;
+ fr_cursor_t cursor;
+
+ int ret;
+
+ fr_cursor_init(&cursor, &request->packet->vps);
+ fr_cursor_tail(&cursor);
+
+ ret = fr_aka_sim_decode(request,
+ &cursor,
+ dict_eap_aka_sim,
+ eap_session->this_round->response->type.data,
+ eap_session->this_round->response->type.length,
+ &ctx);
+ /*
+ * RFC 4187 says we *MUST* notify, not just
+ * send an EAP-Failure in this case where
+ * we cannot decode an EAP-AKA packet.
+ */
+ if (ret < 0) {
+ RPEDEBUG2("Failed decoding attributes");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+ /* vps is the data from the client */
+ aka_vps = fr_cursor_next(&cursor);
+ if (aka_vps && RDEBUG_ENABLED2) {
+ RDEBUG2("Decoded attributes");
+ log_request_pair_list(L_DBG_LVL_2, request, aka_vps, NULL);
+ }
+
+ *subtype_vp = fr_pair_find_by_da(aka_vps, attr_eap_aka_sim_subtype, TAG_ANY);
+ if (!*subtype_vp) {
+ REDEBUG("Missing AT_SUBTYPE");
+ goto failure;
+ }
+ *vps = aka_vps;
+
+ RDEBUG2("Received EAP-Response/%pV", &(*subtype_vp)->data);
+
+ return RLM_MODULE_OK;
+}
+
+/** FAILURE state - State machine exit point after sending EAP-Failure
+ *
+ * Should never actually be called. Is just a placeholder function to represent the FAILURE
+ * termination state. Could equally be a NULL pointer, but then on a logic error
+ * we'd get a SEGV instead of a more friendly assert/failure rcode.
+ */
+static rlm_rcode_t common_eap_failure(UNUSED void *instance, UNUSED void *thread, UNUSED REQUEST *request)
+{
+ rad_assert(0); /* Should never actually be called */
+ return RLM_MODULE_FAIL;
+}
+
+/** FAILURE-NOTIFICATION state - Continue the state machine after receiving a response to our EAP-Request/(AKA|SIM)-Notification
+ *
+ * - Continue based on received AT_SUBTYPE value:
+ * - EAP-Response/SIM-Client-Error - Call 'recv Failure-Notification-Ack { ... }'
+ * - Anything else, enter the FAILURE-NOTIFICATION state.
+ */
+static rlm_rcode_t common_failure_notification(void *instance, UNUSED void *thread, REQUEST *request)
+{
+ rlm_rcode_t rcode;
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+
+ VALUE_PAIR *subtype_vp = NULL;
+ VALUE_PAIR *vps;
+
+ rcode = common_decode(&subtype_vp, &vps, inst, request);
+ if (rcode != RLM_MODULE_OK) return rcode;
+
+#ifdef __clang_analyzer__
+ rad_assert(subtype_vp);
+#endif
+ switch (subtype_vp->vp_uint16) {
+ case FR_SUBTYPE_VALUE_AKA_SIM_NOTIFICATION:
+ RDEBUG2("Failure-Notification ACKed, sending EAP-Failure");
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_failure_notification_ack,
+ RLM_MODULE_NOOP,
+ common_failure_notification_recv_resume,
+ mod_signal,
+ NULL);
+
+ default:
+ RWDEBUG("Failure-Notification not ACKed correctly, sending EAP-Failure anyway");
+ return common_eap_failure_enter(inst, request, eap_session);
+ }
+}
+
+/** SUCCESS state - State machine exit point after sending EAP-Success
+ *
+ * Should never actually be called. Is just a placeholder function to represent the FAILURE
+ * termination state. Could equally be a NULL pointer, but then on a logic error
+ * we'd get a SEGV instead of a more friendly assert/failure rcode.
+ */
+static rlm_rcode_t common_eap_success(UNUSED void *instance, UNUSED void *thread, UNUSED REQUEST *request)
+{
+ rad_assert(0);
+ return RLM_MODULE_FAIL;
+}
+
+/** SUCCESS-NOTIFICATION state - Continue the state machine after receiving a response to our EAP-Request/(AKA|SIM)-Notification
+ *
+ * - Call 'recv Success-Notification-Ack { ... }'
+ */
+static rlm_rcode_t common_success_notification(void *instance, UNUSED void *thread, REQUEST *request)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_success_notification_ack,
+ RLM_MODULE_NOOP,
+ common_success_notification_ack_recv_resume,
+ mod_signal,
+ NULL);
+}
+
+
+/** REAUTHENTICATION state - Continue the state machine after receiving a response to our EAP-Request/SIM-Start
+ *
+ * - Continue based on received AT_SUBTYPE value:
+ * - EAP-Response/(SIM|AKA)-Reauthentication - call 'recv Reauthentication-Response { ... }'
+ * - EAP-Response/(SIM|AKA)-Client-Error - call 'recv Client-Error { ... }' and after that
+ * send a EAP-Request/(SIM|AKA)-Notification indicating a General Failure.
+ * - Anything else, enter the FAILURE-NOTIFICATION state.
+ */
+static rlm_rcode_t common_reauthentication(void *instance, UNUSED void *thread, REQUEST *request)
+{
+ rlm_rcode_t rcode;
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ VALUE_PAIR *subtype_vp = NULL;
+ VALUE_PAIR *from_peer;
+
+ rcode = common_decode(&subtype_vp, &from_peer, inst, request);
+ if (rcode != RLM_MODULE_OK) return rcode;
+
+#ifdef __clang_analyzer__
+ rad_assert(subtype_vp);
+#endif
+ /*
+ * These aren't allowed in Reauthentication responses as they don't apply:
+ *
+ * EAP_AKA_AUTHENTICATION_REJECT - We didn't provide an AUTN value
+ * EAP_AKA_SYNCHRONIZATION_FAILURE - We didn't use new vectors.
+ */
+ switch (subtype_vp->vp_uint16) {
+ case FR_SUBTYPE_VALUE_AKA_SIM_REAUTHENTICATION:
+ /*
+ * AT_COUNTER_TOO_SMALL is handled
+ * in common_reauthentication_response_process.
+ */
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_reauthentication_response,
+ RLM_MODULE_NOOP,
+ common_reauthentication_response_recv_resume,
+ mod_signal,
+ NULL);
+
+ /*
+ * Case 1 where we're allowed to send an EAP-Failure
+ */
+ case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
+ client_error_debug(request, from_peer);
+
+ eap_aka_sim_session->allow_encrypted = false;
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_client_error,
+ RLM_MODULE_NOOP,
+ common_client_error_recv_resume,
+ mod_signal,
+ NULL);
+
+ /*
+ * RFC 4187 says we *MUST* notify, not just
+ * send an EAP-Failure in this case.
+ */
+ default:
+ REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
+ eap_aka_sim_session->allow_encrypted = false;
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+}
+
+/** AKA-CHALLENGE state - Continue the state machine after receiving a response to our EAP-Request/SIM-Challenge
+ *
+ * - Continue based on received AT_SUBTYPE value:
+ * - EAP-Response/AKA-Challenge - call 'recv Challenge-Response { ... }'.
+ * - EAP-Response/AKA-Authentication-Reject - call 'recv Authentication-Reject { ... }' and after that
+ * send a EAP-Request/SIM-Notification indicating a General Failure.
+ * - EAP-Response/AKA-Syncronization-Failure - call 'recv Syncronization-Failure { ... }'.
+ * - EAP-Response/AKA-Client-Error - call 'recv Client-Error { ... }' and after that
+ * send a EAP-Request/AKA-Notification indicating a General Failure.
+ * - Anything else, enter the FAILURE-NOTIFICATION state.
+ */
+static rlm_rcode_t aka_challenge(void *instance, UNUSED void *thread, REQUEST *request)
+{
+ rlm_rcode_t rcode;
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque, eap_aka_sim_session_t);
+
+ VALUE_PAIR *subtype_vp = NULL;
+ VALUE_PAIR *vp;
+ VALUE_PAIR *from_peer;
+
+ rcode = common_decode(&subtype_vp, &from_peer, inst, request);
+ if (rcode != RLM_MODULE_OK) return rcode;
+
+#ifdef __clang_analyzer__
+ rad_assert(subtype_vp);
+#endif
+ switch (subtype_vp->vp_uint16) {
+ case FR_SUBTYPE_VALUE_AKA_CHALLENGE:
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_challenge_response,
+ RLM_MODULE_NOOP,
+ aka_challenge_response_recv_resume,
+ mod_signal,
+ NULL);
+
+ /*
+ * Case 2 where we're allowed to send an EAP-Failure
+ */
+ case FR_SUBTYPE_VALUE_AKA_AUTHENTICATION_REJECT:
+ eap_aka_sim_session->allow_encrypted = false;
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.aka.recv_authentication_reject,
+ RLM_MODULE_NOOP,
+ aka_authentication_reject_recv_resume,
+ mod_signal,
+ NULL);
+
+ case FR_SUBTYPE_VALUE_AKA_SYNCHRONIZATION_FAILURE:
+ {
+ uint64_t new_sqn;
+
+ eap_aka_sim_session->allow_encrypted = false;
+
+ vp = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_auts, TAG_ANY);
+ if (!vp) {
+ REDEBUG("EAP-Response/AKA-Synchronisation-Failure missing AT_AUTS");
+ failure:
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ switch (fr_aka_sim_umts_resync_from_attrs(&new_sqn,
+ request, vp, &eap_aka_sim_session->keys)) {
+ /*
+ * Add everything back that we'll need in the
+ * next challenge round.
+ */
+ case 0:
+ MEM(pair_add_control(&vp, attr_sim_sqn) >= 0);
+ vp->vp_uint64 = new_sqn;
+
+ MEM(pair_add_control(&vp, attr_sim_ki) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.auc.ki,
+ sizeof(eap_aka_sim_session->keys.auc.ki), false);
+
+ MEM(pair_add_control(&vp, attr_sim_opc) >= 0);
+ fr_pair_value_memcpy(vp, eap_aka_sim_session->keys.auc.opc,
+ sizeof(eap_aka_sim_session->keys.auc.opc), false);
+ break;
+
+ case 1: /* Don't have Ki or OPc so something else will need to deal with this */
+ break;
+
+ default:
+ case -1:
+ goto failure;
+ }
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.aka.recv_syncronization_failure,
+ RLM_MODULE_NOOP,
+ aka_synchronization_failure_recv_resume,
+ mod_signal,
+ NULL);
+ }
+
+ /*
+ * Case 1 where we're allowed to send an EAP-Failure
+ */
+ case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
+ client_error_debug(request, from_peer);
+
+ eap_aka_sim_session->allow_encrypted = false;
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_client_error,
+ RLM_MODULE_NOOP,
+ common_client_error_recv_resume,
+ mod_signal,
+ NULL);
+
+ /*
+ * RFC 4187 says we *MUST* notify, not just
+ * send an EAP-Failure in this case.
+ */
+ default:
+ REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
+ eap_aka_sim_session->allow_encrypted = false;
+ goto failure;
+ }
+}
+
+/** SIM-CHALLENGE state - Continue the state machine after receiving a response to our EAP-Request/SIM-Challenge
+ *
+ * - Continue based on received AT_SUBTYPE value:
+ * - EAP-Response/SIM-Challenge - call 'recv Challenge-Response { ... }'.
+ * - EAP-Response/SIM-Client-Error - call 'recv Client-Error { ... }' and after that
+ * send a EAP-Request/SIM-Notification indicating a General Failure.
+ * - Anything else, enter the FAILURE-NOTIFICATION state.
+ */
+static rlm_rcode_t sim_challenge(void *instance, UNUSED void *thread, REQUEST *request)
+{
+ rlm_rcode_t rcode;
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ VALUE_PAIR *subtype_vp = NULL;
+ VALUE_PAIR *from_peer;
+
+ rcode = common_decode(&subtype_vp, &from_peer, inst, request);
+ if (rcode != RLM_MODULE_OK) return rcode;
+
+#ifdef __clang_analyzer__
+ rad_assert(subtype_vp);
+#endif
+ switch (subtype_vp->vp_uint16) {
+ case FR_SUBTYPE_VALUE_SIM_CHALLENGE:
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_challenge_response,
+ RLM_MODULE_NOOP,
+ sim_challenge_response_recv_resume,
+ mod_signal,
+ NULL);
+
+ /*
+ * Case 1 where we're allowed to send an EAP-Failure
+ */
+ case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
+ client_error_debug(request, from_peer);
+
+ eap_aka_sim_session->allow_encrypted = false;
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_client_error,
+ RLM_MODULE_NOOP,
+ common_client_error_recv_resume,
+ mod_signal,
+ NULL);
+
+ /*
+ * RFC 4186 says we *MUST* notify, not just
+ * send an EAP-Failure in this case.
+ */
+ default:
+ REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
+
+ eap_aka_sim_session->allow_encrypted = false;
+
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+}
+
+/** AKA-IDENTITY state - Continue the state machine after receiving a response to our EAP-Request/AKA-Identity
+ *
+ * - Continue based on received AT_SUBTYPE value:
+ * - EAP-Response/AKA-Identity - call either 'recv Identity-Response { ... }' or if
+ * provided 'recv AKA-Identity-Response { ... }'. The idea here is that the
+ * EAP-Identity-Response is really the first round in identity negotiation and
+ * there's no real value distinguishing between the first round and subsequent
+ * rounds, but if users do want to run different logic, then give them a way of
+ * doing that.
+ * - EAP-Response/AKA-Client-Error - call 'recv Client-Error { ... }' and after that
+ * send a EAP-Request/SIM-Notification indicating a General Failure.
+ * - Anything else, enter the FAILURE-NOTIFICATION state.
+ */
+static rlm_rcode_t aka_identity(void *instance, UNUSED void *thread, REQUEST *request)
+{
+ rlm_rcode_t rcode;
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ VALUE_PAIR *subtype_vp = NULL;
+ VALUE_PAIR *from_peer;
+
+ rcode = common_decode(&subtype_vp, &from_peer, inst, request);
+ if (rcode != RLM_MODULE_OK) return rcode;
+
+#ifdef __clang_analyzer__
+ rad_assert(subtype_vp);
+#endif
+
+ switch (subtype_vp->vp_uint16) {
+ /*
+ * This is the subtype we expect
+ */
+ case FR_SUBTYPE_VALUE_AKA_IDENTITY:
+ {
+ VALUE_PAIR *id;
+ fr_aka_sim_id_type_t type;
+
+ id = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity, TAG_ANY);
+ if (!id) {
+ /*
+ * 9.2. EAP-Response/Identity
+ *
+ * The peer sends EAP-Response/Identity in response to a valid
+ * EAP-Request/Identity from the server.
+ * The peer MUST include the AT_IDENTITY attribute. The usage of
+ * AT_IDENTITY is defined in Section 4.1.
+ */
+ REDEBUG("EAP-Response/Identity does not contain AT_IDENTITY");
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ /*
+ * Add ID hint attributes to the request to help
+ * the user make policy decisions.
+ */
+ identity_hint_pairs_add(&type, NULL, request, id->vp_strvalue);
+ if (type == AKA_SIM_ID_TYPE_PERMANENT) {
+ identity_to_permanent_identity(request, id,
+ eap_aka_sim_session->type,
+ inst->strip_permanent_identity_hint);
+ }
+
+ /*
+ * Update cryptographic identity
+ */
+ identity_to_crypto_identity(request, eap_aka_sim_session,
+ (uint8_t const *)id->vp_strvalue, id->vp_length);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.aka.recv_aka_identity_response?
+ inst->actions.aka.recv_aka_identity_response:
+ inst->actions.recv_identity_response,
+ RLM_MODULE_NOOP,
+ aka_identity_response_recv_resume,
+ mod_signal,
+ NULL);
+ }
+
+ /*
+ * Case 1 where we're allowed to send an EAP-Failure
+ *
+ * This can happen in the case of a conservative
+ * peer, where it refuses to provide the permanent
+ * identity.
+ */
+ case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
+ client_error_debug(request, from_peer);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_client_error,
+ RLM_MODULE_NOOP,
+ common_client_error_recv_resume,
+ mod_signal,
+ NULL);
+
+ default:
+ /*
+ * RFC 4187 says we *MUST* notify, not just
+ * send an EAP-Failure in this case.
+ */
+ REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+}
+
+/** SIM-START state - Continue the state machine after receiving a response to our EAP-Request/SIM-Start
+ *
+ * - Continue based on received AT_SUBTYPE value:
+ * - EAP-Response/SIM-Start - call either 'recv Identity-Response { ... }' or if
+ * provided 'recv SIM-Start-Response { ... }'. The idea here is that the
+ * EAP-Identity-Response is really the first round in identity negotiation and
+ * there's no real value distinguishing between the first round and subsequent
+ * rounds, but if users do want to run different logic, then give them a way of
+ * doing that.
+ * - EAP-Response/SIM-Client-Error - call 'recv Client-Error { ... }' and after that
+ * send a EAP-Request/SIM-Notification indicating a General Failure.
+ * - Anything else, enter the FAILURE-NOTIFICATION state.
+ */
+static rlm_rcode_t sim_start(void *instance, UNUSED void *thread, REQUEST *request)
+{
+ rlm_rcode_t rcode;
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+
+ VALUE_PAIR *subtype_vp = NULL;
+ VALUE_PAIR *from_peer;
+
+ rcode = common_decode(&subtype_vp, &from_peer, inst, request);
+ if (rcode != RLM_MODULE_OK) return rcode;
+
+#ifdef __clang_analyzer__
+ rad_assert(subtype_vp);
+#endif
+
+ switch (subtype_vp->vp_uint16) {
+ case FR_SUBTYPE_VALUE_SIM_START:
+ {
+ VALUE_PAIR *id;
+ fr_aka_sim_id_type_t type;
+
+ id = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity, TAG_ANY);
+ if (!id) {
+ /*
+ * RFC 4186 Section #9.2
+ *
+ * The peer sends EAP-Response/SIM/Start in response to a valid
+ * EAP-Request/SIM/Start from the server.
+ * The peer MUST include the AT_IDENTITY attribute. The usage of
+ * AT_IDENTITY is defined in Section 4.1.
+ */
+ REDEBUG("EAP-Response/SIM/Start does not contain AT_IDENTITY");
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ /*
+ * Add ID hint attributes to the request to help
+ * the user make policy decisions.
+ */
+ identity_hint_pairs_add(&type, NULL, request, id->vp_strvalue);
+ if (type == AKA_SIM_ID_TYPE_PERMANENT) {
+ identity_to_permanent_identity(request, id,
+ eap_aka_sim_session->type,
+ inst->strip_permanent_identity_hint);
+ }
+
+ /*
+ * Update cryptographic identity
+ */
+ identity_to_crypto_identity(request, eap_aka_sim_session,
+ (uint8_t const *)id->vp_strvalue, id->vp_length);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.sim.recv_sim_start_response?
+ inst->actions.sim.recv_sim_start_response:
+ inst->actions.recv_identity_response,
+ RLM_MODULE_NOOP,
+ sim_start_response_recv_resume,
+ mod_signal,
+ NULL);
+ }
+
+ /*
+ * Case 1 where we're allowed to send an EAP-Failure
+ *
+ * This can happen in the case of a conservative
+ * peer, where it refuses to provide the permanent
+ * identity.
+ */
+ case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
+ client_error_debug(request, from_peer);
+
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_client_error,
+ RLM_MODULE_NOOP,
+ common_client_error_recv_resume,
+ mod_signal,
+ NULL);
+
+ default:
+ /*
+ * RFC 4187 says we *MUST* notify, not just
+ * send an EAP-Failure in this case.
+ */
+ REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+}
+
+
+/** Resume after 'recv Identity-Response { ... }'
+ *
+ * - Perform the majority of eap_aka_sim_session_t initialisation.
+ * - If 'recv Identity-Response { ... }' returned a failure rcode, enter the FAILURE-NOTIFICATION state.
+ * - ...or continue based on the identity hint byte in the AT_IDENTITY value or EAP-Identity-Response value:
+ * - If identity is a pseudonym, call load pseudonym { ... }.
+ * - If identity is a fastauth identity, enter the REAUTHENTICATE state.
+ * - If identity is a permanent identity, enter the CHALLENGE state.
+ */
+static rlm_rcode_t common_eap_identity_resume(void *instance, UNUSED void *thread, REQUEST *request, UNUSED void *rctx)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(eap_session->opaque,
+ eap_aka_sim_session_t);
+ VALUE_PAIR *eap_type, *method, *identity_type;
+ fr_aka_sim_method_hint_t running, hinted;
+ VALUE_PAIR *from_peer = request->packet->vps;
+
+ section_rcode_process(inst, request, eap_session, eap_aka_sim_session);
+
+ /*
+ * Ignore attempts to change the EAP-Type
+ * This must be done before we enter
+ * the submodule.
+ */
+ eap_type = fr_pair_find_by_da(request->control, attr_eap_type, TAG_ANY);
+ if (eap_type) RWDEBUG("Ignoring &control:EAP-Type, this must be set *before* the EAP module is called");
+
+ method = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_method_hint, TAG_ANY);
+
+ /*
+ * Set default configuration, we may allow these
+ * to be toggled by attributes later.
+ */
+ eap_aka_sim_session->send_result_ind = inst->protected_success;
+ eap_aka_sim_session->id_req = AKA_SIM_NO_ID_REQ; /* Set the default */
+
+ /*
+ * Unless AKA-Prime is explicitly disabled,
+ * use it... It has stronger keying, and
+ * binds authentication to the network.
+ */
+ switch (eap_session->type) {
+ case FR_EAP_METHOD_SIM:
+ RDEBUG2("New EAP-SIM session");
+
+ running = AKA_SIM_METHOD_HINT_SIM;
+
+ eap_aka_sim_session->type = FR_EAP_METHOD_SIM;
+ eap_aka_sim_session->mac_md = EVP_sha1(); /* no checkcode support, so no checkcode_md */
+
+ /*
+ * RFC 5448 makes no mention of being
+ * able to use this with EAP-SIM, so it's
+ * permanently disabled for that EAP method.
+ */
+ eap_aka_sim_session->send_at_bidding_prefer_prime = false;
+ break;
+
+ case FR_EAP_METHOD_AKA:
+ RDEBUG2("New EAP-AKA session");
+
+ running = AKA_SIM_METHOD_HINT_AKA;
+
+ eap_aka_sim_session->type = FR_EAP_METHOD_AKA;
+ eap_aka_sim_session->checkcode_md = eap_aka_sim_session->mac_md = EVP_sha1();
+ eap_aka_sim_session->send_at_bidding_prefer_prime = inst->send_at_bidding_prefer_prime;
+ break;
+
+ case FR_EAP_METHOD_AKA_PRIME:
+ RDEBUG2("New EAP-AKA' session");
+
+ running = AKA_SIM_METHOD_HINT_AKA_PRIME;
+
+ eap_aka_sim_session->type = FR_EAP_METHOD_AKA_PRIME;
+ eap_aka_sim_session->kdf = FR_KDF_VALUE_PRIME_WITH_CK_PRIME_IK_PRIME;
+ eap_aka_sim_session->checkcode_md = eap_aka_sim_session->mac_md = EVP_sha256();
+ break;
+
+ default:
+ rad_assert(0);
+ }
+
+ /*
+ * Warn the user if the selected identity
+ * does not match what's hinted.
+ */
+ if (method) {
+ switch (method->vp_uint32) {
+ case FR_METHOD_HINT_VALUE_AKA_PRIME:
+ hinted = AKA_SIM_METHOD_HINT_AKA_PRIME;
+ break;
+
+ case FR_METHOD_HINT_VALUE_AKA:
+ hinted = AKA_SIM_METHOD_HINT_AKA;
+ break;
+
+ case FR_METHOD_HINT_VALUE_SIM:
+ hinted = AKA_SIM_METHOD_HINT_SIM;
+ break;
+
+ default:
+ hinted = running;
+ break;
+ }
+
+ if (hinted != running) {
+ RWDEBUG("EAP-Identity hints that EAP-%s should be started, but we're attempting EAP-%s",
+ fr_table_str_by_value(fr_aka_sim_id_method_table, hinted, "<INVALID>"),
+ fr_table_str_by_value(fr_aka_sim_id_method_table, running, "<INVALID>"));
+ }
+ }
+
+ /*
+ * We always start by requesting any ID
+ * initially as we can always negotiate down.
+ */
+ if (!identity_req_set_by_user(request, eap_aka_sim_session)) {
+ if (request->rcode == RLM_MODULE_NOTFOUND) {
+ eap_aka_sim_session->id_req = AKA_SIM_ANY_ID_REQ;
+ RDEBUG2("Previous section returned (%s), requesting additional identity (%s)",
+ fr_table_str_by_value(rcode_table, request->rcode, "<INVALID>"),
+ fr_table_str_by_value(fr_aka_sim_id_request_table, eap_aka_sim_session->id_req, "<INVALID>"));
+ } else if (inst->request_identity != AKA_SIM_NO_ID_REQ) {
+ eap_aka_sim_session->id_req = inst->request_identity;
+ RDEBUG2("Requesting additional identity (%s)",
+ fr_table_str_by_value(fr_aka_sim_id_request_table, eap_aka_sim_session->id_req, "<INVALID>"));
+ }
+ }
+
+ /*
+ * User may want us to always request an identity
+ * initially. The RFCs says this is also the
+ * better way to operate, as the supplicant
+ * can 'decorate' the identity in the identity
+ * response.
+ */
+ if (eap_aka_sim_session->id_req != AKA_SIM_NO_ID_REQ) return common_identity_enter(inst, request, eap_session);
+
+ /*
+ * If the identity looks like a fast re-auth id
+ * run fast re-auth, otherwise do a fullauth.
+ */
+ identity_type = fr_pair_find_by_da(from_peer, attr_eap_aka_sim_identity_type, TAG_ANY);
+ if (identity_type) switch (identity_type->vp_uint32) {
+ case FR_IDENTITY_TYPE_VALUE_FASTAUTH:
+ return common_reauthentication_enter(inst, request, eap_session);
+
+ /*
+ * It's a pseudonym, which now needs resolving.
+ * The resume function here calls aka_challenge_enter
+ * if pseudonym resolution went ok.
+ */
+ case FR_IDENTITY_TYPE_VALUE_PSEUDONYM:
+ return unlang_module_yield_to_section(request,
+ inst->actions.load_pseudonym,
+ RLM_MODULE_NOOP,
+ pseudonym_load_resume,
+ mod_signal,
+ (void *)common_challenge_enter);
+
+ case FR_IDENTITY_TYPE_VALUE_PERMANENT:
+ /* FALL-THROUGH */
+
+ default:
+ break;
+ }
+
+ return common_challenge_enter(inst, request, eap_session);
+}
+
+/** Zero out the eap_aka_sim_session when we free it to clear knowledge of secret keys
+ *
+ * @param[in] eap_aka_sim_session to free.
+ * @return 0
+ */
+static int _eap_aka_sim_session_free(eap_aka_sim_session_t *eap_aka_sim_session)
+{
+ memset(eap_aka_sim_session, 0, sizeof(*eap_aka_sim_session));
+ return 0;
+}
+
+/** Enter the EAP-IDENTITY state - State machine entry point
+ *
+ * - Process the incoming EAP-Identity-Response
+ * - Start EAP-SIM/EAP-AKA/EAP-AKA' state machine optionally calling 'recv Identity-Response { ... }'
+ */
+rlm_rcode_t aka_sim_state_machine_start(void *instance, UNUSED void *thread, REQUEST *request)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+ eap_session_t *eap_session = eap_session_get(request->parent);
+ eap_aka_sim_session_t *eap_aka_sim_session;
+ VALUE_PAIR *vp;
+ fr_aka_sim_id_type_t type;
+
+
+ MEM(eap_aka_sim_session = talloc_zero(eap_session, eap_aka_sim_session_t));
+ talloc_set_destructor(eap_aka_sim_session, _eap_aka_sim_session_free);
+
+ eap_session->opaque = eap_aka_sim_session;
+
+ /*
+ * This value doesn't have be strong, but it is
+ * good if it is different now and then.
+ */
+ eap_aka_sim_session->id = (fr_rand() & 0xff);
+
+ /*
+ * Verify we received an EAP-Response/Identity
+ * message before the supplicant started sending
+ * EAP-SIM/AKA/AKA' packets.
+ */
+ if (!eap_session->identity) {
+ REDEBUG("All SIM or AKA exchanges must begin with a EAP-Response/Identity message");
+ return common_failure_notification_enter(inst, request, eap_session);
+ }
+
+ /*
+ * Add ID hint attributes to the request to help
+ * the user make policy decisions.
+ */
+
+ /*
+ * Copy the EAP-Identity into and Identity
+ * attribute to make policies easier.
+ */
+ MEM(pair_add_request(&vp, attr_eap_aka_sim_identity) >= 0);
+ fr_pair_value_bstrncpy(vp, eap_session->identity, talloc_array_length(eap_session->identity) - 1);
+
+ /*
+ * Add ID hint attributes to the request to help
+ * the user make policy decisions.
+ */
+ identity_hint_pairs_add(&type, NULL, request, eap_session->identity);
+ if (type == AKA_SIM_ID_TYPE_PERMANENT) {
+ identity_to_permanent_identity(request, vp, eap_session->type,
+ inst->strip_permanent_identity_hint);
+ }
+
+ identity_to_crypto_identity(request, eap_aka_sim_session,
+ (uint8_t const *)eap_session->identity,
+ talloc_array_length(eap_session->identity) - 1);
+
+ /*
+ * Running the same section as Identity-Response
+ * makes policies significantly easier.
+ */
+ return unlang_module_yield_to_section(request,
+ inst->actions.recv_identity_response,
+ RLM_MODULE_NOOP,
+ common_eap_identity_resume,
+ mod_signal,
+ NULL);
+}
--- /dev/null
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file lib/eap_aka_sim/state_machine.h
+ * @brief Declarations for EAP-AKA
+ *
+ * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ *
+ * @copyright 2016-2019 The FreeRADIUS server project
+ * @copyright 2016-2019 Network RADIUS SARL <sales@networkradius.com>
+ */
+RCSIDH(lib_eap_aka_sim_state_machine_h, "$Id$")
+
+#include <freeradius-devel/eap_aka_sim/base.h>
+
+/** Cache sections to call on various protocol events
+ *
+ */
+typedef struct {
+ union {
+ struct {
+ CONF_SECTION *send_aka_identity_request; //!< Called when we're about to request a
+ ///< different identity.
+ CONF_SECTION *recv_aka_identity_response; //!< Called when we receive a new identity.
+
+ CONF_SECTION *recv_authentication_reject; //!< Called if the supplicant rejects the
+ ///< authentication attempt.
+ CONF_SECTION *recv_syncronization_failure; //!< Called if the supplicant determines
+ ///< the AUTN value is invalid.
+ ///< Usually used for resyncing with the HLR.
+ } aka;
+
+ struct {
+ CONF_SECTION *send_sim_start_request; //!< Called when we're about to request a
+ ///< different identity.
+ CONF_SECTION *recv_sim_start_response; //!< Called when we receive a new identity.
+
+ } sim;
+ };
+
+ CONF_SECTION *send_identity_request; //!< Called when we're about to request a
+ ///< different identity.
+ CONF_SECTION *recv_identity_response; //!< Called when we receive a new identity.
+
+ CONF_SECTION *send_challenge_request; //!< Called when we're about to send a
+ ///< a challenge.
+ CONF_SECTION *recv_challenge_response; //!< Called when we receive a response
+ ///< to a previous challenge.
+
+ CONF_SECTION *send_fast_reauth_request; //!< Called when we're about to send a
+ ///< Fast-Reauth-Request.
+ CONF_SECTION *recv_fast_reauth_response; //!< Called when we receive a response
+ ///< to a previous Fast-Reauth-Request.
+
+ CONF_SECTION *recv_client_error; //!< Called if the supplicant experiences
+ ///< an error of some kind.
+
+ CONF_SECTION *send_reauthentication_request; //!< Challenge the supplicant with an MK
+ ///< from an existing session.
+
+ CONF_SECTION *recv_reauthentication_response; //!< Process the reauthentication response
+ ///< from the supplicant.
+
+ CONF_SECTION *send_failure_notification; //!< Called when we're about to send a
+ ///< failure notification.
+ CONF_SECTION *send_success_notification; //!< Called when we're about to send a
+ ///< success notification.
+ CONF_SECTION *recv_failure_notification_ack; //!< Called when the supplicant ACKs our
+ ///< failure notification.
+ CONF_SECTION *recv_success_notification_ack; //!< Called when the supplicant ACKs our
+ ///< success notification.
+
+ CONF_SECTION *send_eap_success; //!< Called when we send an EAP-Success message.
+ CONF_SECTION *send_eap_failure; //!< Called when we send an EAP-Failure message.
+
+ CONF_SECTION *load_pseudonym; //!< Resolve a pseudonym to a permanent ID.
+ CONF_SECTION *store_pseudonym; //!< Store a permanent ID to pseudonym mapping.
+ CONF_SECTION *clear_pseudonym; //!< Clear pseudonym to permanent ID mapping.
+
+ CONF_SECTION *load_session; //!< Load cached authentication vectors.
+ CONF_SECTION *store_session; //!< Store authentication vectors.
+ CONF_SECTION *clear_session; //!< Clear authentication vectors.
+} eap_aka_sim_actions_t;
+
+typedef struct {
+ eap_type_t type; //!< Either FR_TYPE_AKA, or FR_TYPE_AKA_PRIME.
+
+ bool challenge_success; //!< Whether we received the correct
+ ///< challenge response.
+ bool reauthentication_success; //!< Whether we got a valid reauthentication
+ ///< response.
+
+ bool allow_encrypted; //!< Whether we can send encrypted
+ ///< attributes at this phase of the attempt.
+
+ uint16_t failure_type; //!< One of the following values:
+ ///< - FR_NOTIFICATION_VALUE_GENERAL_FAILURE_AFTER_AUTHENTICATION
+ ///< - FR_NOTIFICATION_VALUE_TEMPORARILY_DENIED
+ ///< - FR_NOTIFICATION_VALUE_NOT_SUBSCRIBED
+ ///< - FR_NOTIFICATION_VALUE_GENERAL_FAILURE
+
+ /*
+ * Identity management
+ */
+ char *pseudonym_sent; //!< Pseudonym value we sent.
+ char *fastauth_sent; //!< Fastauth value we sent.
+
+ fr_aka_sim_id_req_type_t id_req; //!< The type of identity we're requesting
+ fr_aka_sim_id_req_type_t last_id_req; //!< The last identity request we sent.
+
+ /*
+ * Per-session configuration
+ */
+
+ bool send_result_ind; //!< Say that we would like to use protected
+ ///< result indications
+ ///< (AKA-Notification-Success).
+ bool send_at_bidding_prefer_prime; //!< Indicate that we prefer EAP-AKA' and
+ ///< include an AT_BIDDING attribute.
+
+ bool prev_recv_sync_failure; //!< We only allow one sync failure per
+ ///< session for sanity.
+
+
+ fr_aka_sim_keys_t keys; //!< Various EAP-AKA/AKA'/SIMkeys.
+
+ EVP_MD const *checkcode_md; //!< Message digest we use to generate the
+ ///< checkcode. EVP_sha1() for EAP-AKA/SIM,
+ ///< EVP_sha256() for EAP-AKA'.
+ fr_aka_sim_checkcode_t *checkcode_state; //!< Digest of all identity packets we've seen.
+ uint8_t checkcode[32]; //!< Checkcode we calculated.
+ size_t checkcode_len; //!< 0, 20 or 32 bytes.
+
+
+ uint16_t kdf; //!< The key derivation function used to derive
+ ///< session keys.
+
+ EVP_MD const *mac_md; //!< HMAC-MD we use to generate the MAC.
+ ///< EVP_sha1() for EAP-AKA, EVP_sha256()
+ ///< for EAP-AKA'.
+
+ int id; //!< Packet ID. (replay protection).
+} eap_aka_sim_session_t;
+
+typedef struct {
+ char const *network_name; //!< Network ID as described by RFC 5448.
+ fr_aka_sim_id_req_type_t request_identity; //!< Whether we always request the identity of
+ ///< the subscriber.
+ size_t ephemeral_id_length; //!< The length of any identities we're
+ ///< generating.
+ CONF_SECTION *virtual_server; //!< Virtual server.
+ bool protected_success; //!< Send a success notification as well as
+ ///< and EAP-Success packet.
+ bool send_at_bidding_prefer_prime; //!< Include the AT bidding attribute in
+ ///< challenge requests.
+ bool send_at_bidding_prefer_prime_is_set; //!< Whether the user specified a value.
+
+ bool strip_permanent_identity_hint; //!< Control whether the hint byte is stripped
+ ///< when populating Permanent-Identity.
+
+ eap_aka_sim_actions_t actions; //!< Pre-compiled virtual server sections.
+} eap_aka_sim_common_conf_t;
+
+/*
+ * The main entry point
+ */
+rlm_rcode_t aka_sim_state_machine_start(void *instance, UNUSED void *thread, REQUEST *request);
/**
* $Id$
- * @file src/lib/sim/vector.c
+ * @file src/lib/aka-sim/vector.c
* @brief Retrieve or derive vectors for EAP-SIM.
*
* @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
#include <freeradius-devel/eap/base.h>
#include <freeradius-devel/eap/types.h>
+#include <freeradius-devel/sim/common.h>
+#include <freeradius-devel/sim/milenage.h>
+#include <freeradius-devel/sim/ts_34_108.h>
+#include <freeradius-devel/sim/comp128.h>
+#include <freeradius-devel/protocol/freeradius/freeradius.internal.sim.h>
+
#include "base.h"
#include "attrs.h"
-#include "comp128.h"
-#include "milenage.h"
#include <freeradius-devel/server/rad_assert.h>
return 1;
}
-static int vector_gsm_from_ki(eap_session_t *eap_session, VALUE_PAIR *vps, int idx, fr_sim_keys_t *keys)
+static int vector_gsm_from_ki(REQUEST *request, VALUE_PAIR *vps, int idx, fr_aka_sim_keys_t *keys)
{
- REQUEST *request = eap_session->request;
VALUE_PAIR *ki_vp, *version_vp;
uint8_t opc_buff[MILENAGE_OPC_SIZE];
uint8_t const *opc_p;
version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version, TAG_ANY);
if (!version_vp) {
if (vector_opc_from_op(request, &opc_p, opc_buff, vps, ki_vp->vp_octets) < 0) return -1;
- version = opc_p ? 4 : 3;
+ version = opc_p ? FR_SIM_ALGO_VERSION_VALUE_COMP128_4 : FR_SIM_ALGO_VERSION_VALUE_COMP128_3;
/*
- * Version we explicitly specified, see if we can find the prerequisite
+ * Version was explicitly specified, see if we can find the prerequisite
* attributes.
*/
} else {
version = version_vp->vp_uint32;
- if (version == 4) {
+ if (version == FR_SIM_ALGO_VERSION_VALUE_COMP128_4) {
if (vector_opc_from_op(request, &opc_p, opc_buff, vps, ki_vp->vp_octets) < 0) return -1;
if (!opc_p) {
RPEDEBUG2("No &control:%s or &control:%s found, "
}
}
- for (i = 0; i < SIM_VECTOR_GSM_RAND_SIZE; i += sizeof(uint32_t)) {
+ for (i = 0; i < AKA_SIM_VECTOR_GSM_RAND_SIZE; i += sizeof(uint32_t)) {
uint32_t rand = fr_rand();
memcpy(&keys->gsm.vector[idx].rand[i], &rand, sizeof(rand));
}
switch (version) {
- case 1:
+ case FR_SIM_ALGO_VERSION_VALUE_COMP128_1:
comp128v1(keys->gsm.vector[idx].sres,
keys->gsm.vector[idx].kc,
ki_vp->vp_octets,
keys->gsm.vector[idx].rand);
break;
- case 2:
+ case FR_SIM_ALGO_VERSION_VALUE_COMP128_2:
comp128v23(keys->gsm.vector[idx].sres,
keys->gsm.vector[idx].kc,
ki_vp->vp_octets,
keys->gsm.vector[idx].rand, true);
break;
- case 3:
+ case FR_SIM_ALGO_VERSION_VALUE_COMP128_3:
comp128v23(keys->gsm.vector[idx].sres,
keys->gsm.vector[idx].kc,
ki_vp->vp_octets,
keys->gsm.vector[idx].rand, false);
break;
- case 4:
+ case FR_SIM_ALGO_VERSION_VALUE_COMP128_4:
if (milenage_gsm_generate(keys->gsm.vector[idx].sres,
keys->gsm.vector[idx].kc,
opc_p,
*/
memcpy(keys->auc.ki, ki_vp->vp_octets, sizeof(keys->auc.ki));
memcpy(keys->auc.opc, opc_p, sizeof(keys->auc.opc));
- keys->vector_src = SIM_VECTOR_SRC_KI;
+ keys->vector_src = AKA_SIM_VECTOR_SRC_KI;
return 0;
}
-static int vector_gsm_from_triplets(eap_session_t *eap_session, VALUE_PAIR *vps,
- int idx, fr_sim_keys_t *keys)
+static int vector_gsm_from_triplets(REQUEST *request, VALUE_PAIR *vps,
+ int idx, fr_aka_sim_keys_t *keys)
{
- REQUEST *request = eap_session->request;
VALUE_PAIR *rand = NULL, *sres = NULL, *kc = NULL;
fr_cursor_t cursor;
int i;
- for (i = 0, (kc = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_sim_kc));
+ for (i = 0, (kc = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_sim_kc));
(i < idx) && (kc = fr_cursor_next(&cursor));
i++);
if (!kc) {
RDEBUG3("No &control:%s[%i] attribute found, not using GSM triplets",
- attr_eap_sim_kc->name, idx);
+ attr_eap_aka_sim_kc->name, idx);
return 1;
}
- if (kc->vp_length != SIM_VECTOR_GSM_KC_SIZE) {
- REDEBUG("&control:%s[%i] is not " STRINGIFY(SIM_VECTOR_GSM_KC_SIZE) " bytes, got %zu bytes",
- attr_eap_sim_kc->name, idx, kc->vp_length);
+ if (kc->vp_length != AKA_SIM_VECTOR_GSM_KC_SIZE) {
+ REDEBUG("&control:%s[%i] is not " STRINGIFY(AKA_SIM_VECTOR_GSM_KC_SIZE) " bytes, got %zu bytes",
+ attr_eap_aka_sim_kc->name, idx, kc->vp_length);
return -1;
}
- for (i = 0, (rand = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_sim_rand));
+ for (i = 0, (rand = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_sim_rand));
(i < idx) && (rand = fr_cursor_next(&cursor));
i++);
if (!rand) {
RDEBUG3("No &control:%s[%i] attribute found, not using GSM triplets",
- attr_eap_sim_rand->name, idx);
+ attr_eap_aka_sim_rand->name, idx);
return 1;
}
- if (rand->vp_length != SIM_VECTOR_GSM_RAND_SIZE) {
+ if (rand->vp_length != AKA_SIM_VECTOR_GSM_RAND_SIZE) {
REDEBUG("&control:EAP-SIM-Rand[%i] is not " STRINGIFY(SIM_RAND_SIZE) " bytes, got %zu bytes",
idx, rand->vp_length);
return -1;
}
- for (i = 0, (sres = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_sim_sres));
+ for (i = 0, (sres = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_sim_sres));
(i < idx) && (sres = fr_cursor_next(&cursor));
i++);
if (!sres) {
RDEBUG3("No &control:%s[%i] attribute found, not using GSM triplets",
- attr_eap_sim_sres->name, idx);
+ attr_eap_aka_sim_sres->name, idx);
return 1;
}
- if (sres->vp_length != SIM_VECTOR_GSM_SRES_SIZE) {
- REDEBUG("&control:%s[%i] is not " STRINGIFY(SIM_VECTOR_GSM_SRES_SIZE) " bytes, got %zu bytes",
- attr_eap_sim_sres->name, idx, sres->vp_length);
+ if (sres->vp_length != AKA_SIM_VECTOR_GSM_SRES_SIZE) {
+ REDEBUG("&control:%s[%i] is not " STRINGIFY(AKA_SIM_VECTOR_GSM_SRES_SIZE) " bytes, got %zu bytes",
+ attr_eap_aka_sim_sres->name, idx, sres->vp_length);
return -1;
}
- memcpy(keys->gsm.vector[idx].kc, kc->vp_strvalue, SIM_VECTOR_GSM_KC_SIZE);
- memcpy(keys->gsm.vector[idx].rand, rand->vp_octets, SIM_VECTOR_GSM_RAND_SIZE);
- memcpy(keys->gsm.vector[idx].sres, sres->vp_octets, SIM_VECTOR_GSM_SRES_SIZE);
- keys->vector_src = SIM_VECTOR_SRC_TRIPLETS;
+ memcpy(keys->gsm.vector[idx].kc, kc->vp_strvalue, AKA_SIM_VECTOR_GSM_KC_SIZE);
+ memcpy(keys->gsm.vector[idx].rand, rand->vp_octets, AKA_SIM_VECTOR_GSM_RAND_SIZE);
+ memcpy(keys->gsm.vector[idx].sres, sres->vp_octets, AKA_SIM_VECTOR_GSM_SRES_SIZE);
+ keys->vector_src = AKA_SIM_VECTOR_SRC_TRIPLETS;
return 0;
}
/** Derive triplets from quintuplets
*
*/
-static int vector_gsm_from_quintuplets(eap_session_t *eap_session, VALUE_PAIR *vps,
- int idx, fr_sim_keys_t *keys)
+static int vector_gsm_from_quintuplets(REQUEST *request, VALUE_PAIR *vps,
+ int idx, fr_aka_sim_keys_t *keys)
{
- REQUEST *request = eap_session->request;
fr_cursor_t cursor;
VALUE_PAIR *ck = NULL, *ik = NULL, *rand = NULL, *xres = NULL;
/*
* Fetch CK
*/
- for (i = 0, (ck = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_ck));
+ for (i = 0, (ck = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_sim_ck));
(i < idx) && (ck = fr_cursor_next(&cursor));
i++);
if (!ck) {
RDEBUG3("No &control:%s[%i] attribute found, not using quintuplet derivation",
- attr_eap_aka_ck->name, idx);
+ attr_eap_aka_sim_ck->name, idx);
return 1;
}
/*
* Fetch IK
*/
- for (i = 0, (ik = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_ik));
+ for (i = 0, (ik = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_sim_ik));
(i < idx) && (ik = fr_cursor_next(&cursor));
i++);
if (!ik) {
RDEBUG3("No &control:%s[%i] attribute found, not using quintuplet derivation",
- attr_eap_aka_ik->name, idx);
+ attr_eap_aka_sim_ik->name, idx);
return 1;
}
/*
* Fetch RAND
*/
- for (i = 0, (rand = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_rand));
+ for (i = 0, (rand = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_sim_rand));
(i < idx) && (rand = fr_cursor_next(&cursor));
i++);
if (!rand) {
RDEBUG3("No &control:%s[%i] attribute found, not using quintuplet derivation",
- attr_eap_aka_rand->name, idx);
+ attr_eap_aka_sim_rand->name, idx);
return 1;
}
- if (rand->vp_length != SIM_VECTOR_UMTS_RAND_SIZE) {
+ if (rand->vp_length != AKA_SIM_VECTOR_UMTS_RAND_SIZE) {
REDEBUG("&control:%s[%i] incorrect length. Expected "
- STRINGIFY(SIM_VECTOR_UMTS_RAND_SIZE) " bytes, "
- "got %zu bytes", attr_eap_aka_rand->name, idx, rand->vp_length);
+ STRINGIFY(AKA_SIM_VECTOR_UMTS_RAND_SIZE) " bytes, "
+ "got %zu bytes", attr_eap_aka_sim_rand->name, idx, rand->vp_length);
return -1;
}
/*
* Fetch XRES
*/
- for (i = 0, (xres = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_xres));
+ for (i = 0, (xres = fr_cursor_iter_by_da_init(&cursor, &vps, attr_eap_aka_sim_xres));
(i < idx) && (xres = fr_cursor_next(&cursor));
i++);
if (!xres) {
RDEBUG3("No &control:%s[%i] attribute found, not using quintuplet derivation",
- attr_eap_aka_xres->name, idx);
+ attr_eap_aka_sim_xres->name, idx);
return 1;
}
- memcpy(keys->gsm.vector[idx].rand, rand->vp_octets, SIM_VECTOR_GSM_RAND_SIZE);
+ memcpy(keys->gsm.vector[idx].rand, rand->vp_octets, AKA_SIM_VECTOR_GSM_RAND_SIZE);
milenage_gsm_from_umts(keys->gsm.vector[idx].sres,
keys->gsm.vector[idx].kc,
ck->vp_octets,
xres->vp_octets);
- keys->vector_src = SIM_VECTOR_SRC_QUINTUPLETS;
+ keys->vector_src = AKA_SIM_VECTOR_SRC_QUINTUPLETS;
return 0;
}
*
* Hunt for a source of SIM triplets
*
- * @param eap_session The current eap_session.
- * @param vps List to hunt for triplets in.
- * @param idx To write EAP-SIM triplets to.
- * @param keys EAP session keys.
- * @param src Forces triplets to be retrieved from a particular src
+ * @param[in] request The current subrequest.
+ * @param[in] vps List to hunt for triplets in.
+ * @param[in] idx To write EAP-SIM triplets to.
+ * @param[in] keys EAP session keys.
+ * @param[in] src Forces triplets to be retrieved from a particular src
* and ensures if multiple triplets are being retrieved
* that they all come from the same src.
* @return
* - 0 Vector was retrieved OK and written to the specified index.
* - -1 Error retrieving vector from the specified src.
*/
-int fr_sim_vector_gsm_from_attrs(eap_session_t *eap_session, VALUE_PAIR *vps,
- int idx, fr_sim_keys_t *keys, fr_sim_vector_src_t *src)
+int fr_aka_sim_vector_gsm_from_attrs(REQUEST *request, VALUE_PAIR *vps,
+ int idx, fr_aka_sim_keys_t *keys, fr_aka_sim_vector_src_t *src)
{
- REQUEST *request = eap_session->request;
int ret;
rad_assert(idx >= 0 && idx < 3);
- rad_assert((keys->vector_type == SIM_VECTOR_NONE) || (keys->vector_type == SIM_VECTOR_GSM));
+ rad_assert((keys->vector_type == AKA_SIM_VECTOR_NONE) || (keys->vector_type == AKA_SIM_VECTOR_GSM));
switch (*src) {
default:
- case SIM_VECTOR_SRC_KI:
- ret = vector_gsm_from_ki(eap_session, vps, idx, keys);
+ case AKA_SIM_VECTOR_SRC_KI:
+ ret = vector_gsm_from_ki(request, vps, idx, keys);
if (ret == 0) {
- *src = SIM_VECTOR_SRC_KI;
+ *src = AKA_SIM_VECTOR_SRC_KI;
break;
}
if (ret < 0) return -1;
- if (*src != SIM_VECTOR_SRC_AUTO) return 1;
+ if (*src != AKA_SIM_VECTOR_SRC_AUTO) return 1;
/* FALL-THROUGH */
- case SIM_VECTOR_SRC_TRIPLETS:
- ret = vector_gsm_from_triplets(eap_session, vps, idx, keys);
+ case AKA_SIM_VECTOR_SRC_TRIPLETS:
+ ret = vector_gsm_from_triplets(request, vps, idx, keys);
if (ret == 0) {
- *src = SIM_VECTOR_SRC_TRIPLETS;
+ *src = AKA_SIM_VECTOR_SRC_TRIPLETS;
break;
}
if (ret < 0) return -1;
- if (*src != SIM_VECTOR_SRC_AUTO) return 1;
+ if (*src != AKA_SIM_VECTOR_SRC_AUTO) return 1;
/* FALL-THROUGH */
- case SIM_VECTOR_SRC_QUINTUPLETS:
- ret = vector_gsm_from_quintuplets(eap_session, vps, idx, keys);
+ case AKA_SIM_VECTOR_SRC_QUINTUPLETS:
+ ret = vector_gsm_from_quintuplets(request, vps, idx, keys);
if (ret == 0) {
- *src = SIM_VECTOR_SRC_QUINTUPLETS;
+ *src = AKA_SIM_VECTOR_SRC_QUINTUPLETS;
break;
}
if (ret < 0) return -1;
/*
* Don't change colon indent, matches other messages later...
*/
- RHEXDUMP_INLINE2(
- keys->gsm.vector[idx].kc, SIM_VECTOR_GSM_KC_SIZE,
- "KC :");
- RHEXDUMP_INLINE2(
- keys->gsm.vector[idx].rand, SIM_VECTOR_GSM_RAND_SIZE,
- "RAND :");
- RHEXDUMP_INLINE2(
- keys->gsm.vector[idx].sres, SIM_VECTOR_GSM_SRES_SIZE,
- "SRES :");
+ RHEXDUMP_INLINE2(keys->gsm.vector[idx].kc, AKA_SIM_VECTOR_GSM_KC_SIZE,
+ "KC :");
+ RHEXDUMP_INLINE2(keys->gsm.vector[idx].rand, AKA_SIM_VECTOR_GSM_RAND_SIZE,
+ "RAND :");
+ RHEXDUMP_INLINE2(keys->gsm.vector[idx].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE,
+ "SRES :");
REXDENT();
}
- keys->vector_type = SIM_VECTOR_GSM;
+ keys->vector_type = AKA_SIM_VECTOR_GSM;
return 0;
}
-static int vector_umts_from_ki(REQUEST *request, VALUE_PAIR *vps, fr_sim_keys_t *keys)
+static int vector_umts_from_ki(REQUEST *request, VALUE_PAIR *vps, fr_aka_sim_keys_t *keys)
{
VALUE_PAIR *ki_vp, *amf_vp, *sqn_vp, *version_vp;
- uint64_t sqn;
- uint8_t amf_buff[MILENAGE_AMF_SIZE] = { 0x00, 0x00 };
- uint8_t opc_buff[MILENAGE_OPC_SIZE];
- uint8_t const *opc_p;
- uint32_t version = 4;
+ size_t ki_size, amf_size;
+ uint32_t version = FR_SIM_ALGO_VERSION_VALUE_MILENAGE;
int i;
+ /*
+ * Select the algorithm (default to Milenage)
+ */
+ version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version, TAG_ANY);
+ if (version_vp) version = version_vp->vp_uint32;
+
+ /*
+ * Get expected input sizes
+ */
+ switch (version) {
+ case FR_SIM_ALGO_VERSION_VALUE_MILENAGE:
+ ki_size = MILENAGE_KI_SIZE;
+ amf_size = MILENAGE_AMF_SIZE;
+ break;
+
+ case FR_SIM_ALGO_VERSION_VALUE_TS_34_108_UMTS:
+ ki_size = TS_34_108_KI_SIZE;
+ amf_size = TS_34_108_AMF_SIZE;
+ break;
+
+ /*
+ * GSM algos
+ */
+ case FR_SIM_ALGO_VERSION_VALUE_COMP128_1:
+ case FR_SIM_ALGO_VERSION_VALUE_COMP128_2:
+ case FR_SIM_ALGO_VERSION_VALUE_COMP128_3:
+ case FR_SIM_ALGO_VERSION_VALUE_COMP128_4:
+ REDEBUG("COMP128-* algorithms cannot generate UMTS vectors");
+ return -1;
+
+ default:
+ REDEBUG("Unknown/unsupported algorithm %i", version);
+ return -1;
+ }
+
+ /*
+ * Find the Ki VP and check its length
+ */
ki_vp = fr_pair_find_by_da(vps, attr_sim_ki, TAG_ANY);
if (!ki_vp) {
RDEBUG3("No &control:%s found, not generating quintuplets locally", attr_sim_ki->name);
return 1;
- } else if (ki_vp->vp_length != MILENAGE_KI_SIZE) {
- REDEBUG("&control:%s has incorrect length, expected %u bytes got %zu bytes",
- attr_sim_ki->name, MILENAGE_KI_SIZE, ki_vp->vp_length);
+ } else if (ki_vp->vp_length != ki_size) {
+ REDEBUG("&control:%s has incorrect length, expected %zu bytes got %zu bytes",
+ attr_sim_ki->name, ki_size, ki_vp->vp_length);
return -1;
}
- if (vector_opc_from_op(request, &opc_p, opc_buff, vps, ki_vp->vp_octets) < 0) return -1;
+ /*
+ * Find the Sequence Number VP or default to SQN = 2
+ */
+ sqn_vp = fr_pair_find_by_da(vps, attr_sim_sqn, TAG_ANY);
+ keys->sqn = sqn_vp ? sqn_vp->vp_uint64 : 2; /* 2 is the lowest valid SQN on our side */
+ /*
+ * Check if we have an AMF value
+ */
amf_vp = fr_pair_find_by_da(vps, attr_sim_amf, TAG_ANY);
if (amf_vp) {
- if (amf_vp->vp_length != sizeof(amf_buff)) {
- REDEBUG("&control:%s has incorrect length, expected %u bytes got %zu bytes",
- attr_sim_amf->name, MILENAGE_AMF_SIZE, amf_vp->vp_length);
+ if (amf_vp->vp_length != amf_size) {
+ REDEBUG("&control:%s has incorrect length, expected %zu bytes got %zu bytes",
+ attr_sim_amf->name, amf_size, amf_vp->vp_length);
return -1;
}
- memcpy(amf_buff, amf_vp->vp_octets, sizeof(amf_buff));
}
- sqn_vp = fr_pair_find_by_da(vps, attr_sim_sqn, TAG_ANY);
- sqn = sqn_vp ? sqn_vp->vp_uint64 : 2;
-
/*
- * We default to milenage
+ * Generate rand
*/
- version_vp = fr_pair_find_by_da(vps, attr_sim_algo_version, TAG_ANY);
- if (version_vp) version = version_vp->vp_uint32;
-
- for (i = 0; i < SIM_VECTOR_UMTS_RAND_SIZE; i += sizeof(uint32_t)) {
+ for (i = 0; i < AKA_SIM_VECTOR_UMTS_RAND_SIZE; i += sizeof(uint32_t)) {
uint32_t rand = fr_rand();
memcpy(&keys->umts.vector.rand[i], &rand, sizeof(rand));
}
switch (version) {
- case 4:
+ case FR_SIM_ALGO_VERSION_VALUE_MILENAGE:
{
- uint8_t sqn_buff[MILENAGE_SQN_SIZE];
+ uint8_t amf_buff[MILENAGE_AMF_SIZE] = { 0x00, 0x00 };
+ uint8_t sqn_buff[MILENAGE_SQN_SIZE];
+ uint8_t opc_buff[MILENAGE_OPC_SIZE];
+ uint8_t const *opc_p;
+
+ if (vector_opc_from_op(request, &opc_p, opc_buff, vps, ki_vp->vp_octets) < 0) return -1;
- keys->sqn = sqn_vp ? sqn_vp->vp_uint64 : 0;
uint48_to_buff(sqn_buff, keys->sqn);
+ if (amf_vp) memcpy(amf_buff, amf_vp->vp_octets, amf_size);
RDEBUG3("Milenage inputs");
RINDENT();
/*
* Don't change colon indent, matches other messages later...
*/
- RHEXDUMP_INLINE3(ki_vp->vp_octets, MILENAGE_KI_SIZE,
+ RHEXDUMP_INLINE3(ki_vp->vp_octets, ki_size,
"Ki :");
- RHEXDUMP_INLINE3(opc_p, MILENAGE_OPC_SIZE,
+ RHEXDUMP_INLINE3(opc_p, sizeof(opc_buff),
"OPc :");
- RHEXDUMP_INLINE3(sqn_buff, MILENAGE_SQN_SIZE,
+ RHEXDUMP_INLINE3(sqn_buff, sizeof(sqn_buff),
"SQN :");
- RHEXDUMP_INLINE3(amf_buff, MILENAGE_AMF_SIZE,
+ RHEXDUMP_INLINE3(amf_buff, sizeof(amf_buff),
"AMF :");
REXDENT();
opc_p,
amf_buff,
ki_vp->vp_octets,
- sqn,
+ keys->sqn,
keys->umts.vector.rand) < 0) {
RPEDEBUG2("Failed deriving UMTS Quintuplet");
return -1;
}
- keys->umts.vector.xres_len = 8;
+ keys->umts.vector.xres_len = MILENAGE_RES_SIZE;
/*
* Store the keys we used for possible AUTS
*/
memcpy(keys->auc.ki, ki_vp->vp_octets, sizeof(keys->auc.ki));
memcpy(keys->auc.opc, opc_p, sizeof(keys->auc.opc));
- keys->vector_src = SIM_VECTOR_SRC_KI;
+ keys->vector_src = AKA_SIM_VECTOR_SRC_KI;
}
return 0;
- case 1:
- case 2:
- case 3:
- REDEBUG("Only Milenage can be used to generate quintuplets");
- return -1;
+ /*
+ * This is a dummy algorithm and should be used for testing
+ * purposes only. It offers no security and can be trivially
+ * broken and the original Ki retrieved.
+ */
+ case FR_SIM_ALGO_VERSION_VALUE_TS_34_108_UMTS:
+ {
+ uint8_t amf_buff[TS_34_108_AMF_SIZE] = { 0x00, 0x00 };
+ uint8_t sqn_buff[TS_34_108_SQN_SIZE];
+
+ uint48_to_buff(sqn_buff, keys->sqn);
+
+ if (amf_vp) memcpy(amf_buff, amf_vp->vp_octets, amf_size);
+
+ RDEBUG3("TS-34-108-UMTS inputs");
+ RINDENT();
+ /*
+ * Don't change colon indent, matches other messages later...
+ */
+ RHEXDUMP_INLINE3(
+ ki_vp->vp_octets, ki_size,
+ "Ki :");
+ RHEXDUMP_INLINE3(
+ sqn_buff, sizeof(sqn_buff),
+ "SQN :");
+ RHEXDUMP_INLINE3(
+ amf_buff, sizeof(amf_buff),
+ "AMF :");
+ REXDENT();
+
+ if (ts_34_108_umts_generate(keys->umts.vector.autn,
+ keys->umts.vector.ik,
+ keys->umts.vector.ck,
+ keys->umts.vector.ak,
+ keys->umts.vector.xres,
+ amf_buff,
+ ki_vp->vp_octets,
+ keys->sqn,
+ keys->umts.vector.rand) < 0) {
+ RPEDEBUG2("Failed deriving UMTS Quintuplet");
+ return -1;
+ }
+ keys->umts.vector.xres_len = TS_34_108_RES_SIZE;
+
+ /*
+ * Store the keys we used for possible AUTS
+ * validation later.
+ */
+ memcpy(keys->auc.ki, ki_vp->vp_octets, sizeof(keys->auc.ki));
+ keys->vector_src = AKA_SIM_VECTOR_SRC_KI;
+ }
+ return 0;
default:
- REDEBUG("Unknown/unsupported algorithm %i", version);
+ rad_assert(0);
return -1;
}
}
/** Get one set of quintuplets from the request
*
*/
-static int vector_umts_from_quintuplets(REQUEST *request, VALUE_PAIR *vps, fr_sim_keys_t *keys)
+static int vector_umts_from_quintuplets(REQUEST *request, VALUE_PAIR *vps, fr_aka_sim_keys_t *keys)
{
VALUE_PAIR *rand_vp = NULL, *xres_vp = NULL, *ck_vp = NULL, *ik_vp = NULL;
VALUE_PAIR *autn_vp = NULL, *sqn_vp = NULL, *ak_vp = NULL;
/*
* Fetch AUTN
*/
- autn_vp = fr_pair_find_by_da(vps, attr_eap_aka_autn, TAG_ANY);
+ autn_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_autn, TAG_ANY);
if (!autn_vp) {
- RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_autn->name);
+ RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_sim_autn->name);
return 1;
}
- if (autn_vp->vp_length > SIM_VECTOR_UMTS_AUTN_SIZE) {
+ if (autn_vp->vp_length > AKA_SIM_VECTOR_UMTS_AUTN_SIZE) {
REDEBUG("&control:%s incorrect length. Expected "
- STRINGIFY(SIM_VECTOR_UMTS_AUTN_SIZE) " bytes, got %zu bytes",
- attr_eap_aka_autn->name, autn_vp->vp_length);
+ STRINGIFY(AKA_SIM_VECTOR_UMTS_AUTN_SIZE) " bytes, got %zu bytes",
+ attr_eap_aka_sim_autn->name, autn_vp->vp_length);
return -1;
}
/*
* Fetch CK
*/
- ck_vp = fr_pair_find_by_da(vps, attr_eap_aka_ck, TAG_ANY);
+ ck_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_ck, TAG_ANY);
if (!ck_vp) {
- RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_ck->name);
+ RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_sim_ck->name);
return 1;
}
- if (ck_vp->vp_length > SIM_VECTOR_UMTS_CK_SIZE) {
+ if (ck_vp->vp_length > AKA_SIM_VECTOR_UMTS_CK_SIZE) {
REDEBUG("&control:%s incorrect length. Expected "
STRINGIFY(EAP_AKA_XRES_MAX_SIZE) " bytes, got %zu bytes",
- attr_eap_aka_ck->name, ck_vp->vp_length);
+ attr_eap_aka_sim_ck->name, ck_vp->vp_length);
return -1;
}
/*
* Fetch IK
*/
- ik_vp = fr_pair_find_by_da(vps, attr_eap_aka_ik, TAG_ANY);
+ ik_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_ik, TAG_ANY);
if (!ik_vp) {
- RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_ik->name);
+ RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_sim_ik->name);
return 1;
}
- if (ik_vp->vp_length > SIM_VECTOR_UMTS_IK_SIZE) {
+ if (ik_vp->vp_length > AKA_SIM_VECTOR_UMTS_IK_SIZE) {
REDEBUG("&control:%s incorrect length. Expected "
- STRINGIFY(SIM_VECTOR_UMTS_IK_SIZE) " bytes, got %zu bytes",
- attr_eap_aka_ik->name, ik_vp->vp_length);
+ STRINGIFY(AKA_SIM_VECTOR_UMTS_IK_SIZE) " bytes, got %zu bytes",
+ attr_eap_aka_sim_ik->name, ik_vp->vp_length);
return -1;
}
/*
* Fetch RAND
*/
- rand_vp = fr_pair_find_by_da(vps, attr_eap_aka_rand, TAG_ANY);
+ rand_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_rand, TAG_ANY);
if (!rand_vp) {
- RDEBUG3("No &control:%s attribute found, not using quintuplet derivation", attr_eap_aka_rand->name);
+ RDEBUG3("No &control:%s attribute found, not using quintuplet derivation", attr_eap_aka_sim_rand->name);
return 1;
}
- if (rand_vp->vp_length != SIM_VECTOR_UMTS_RAND_SIZE) {
- REDEBUG("&control:%s incorrect length. Expected " STRINGIFY(SIM_VECTOR_UMTS_RAND_SIZE) " bytes, "
- "got %zu bytes", attr_eap_aka_rand->name, rand_vp->vp_length);
+ if (rand_vp->vp_length != AKA_SIM_VECTOR_UMTS_RAND_SIZE) {
+ REDEBUG("&control:%s incorrect length. Expected " STRINGIFY(AKA_SIM_VECTOR_UMTS_RAND_SIZE) " bytes, "
+ "got %zu bytes", attr_eap_aka_sim_rand->name, rand_vp->vp_length);
return -1;
}
/*
* Fetch XRES
*/
- xres_vp = fr_pair_find_by_da(vps, attr_eap_aka_xres, TAG_ANY);
+ xres_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_xres, TAG_ANY);
if (!xres_vp) {
- RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_xres->name);
+ RDEBUG3("No &control:%s attribute found, not using UMTS quintuplets", attr_eap_aka_sim_xres->name);
return 1;
}
- if (xres_vp->vp_length > SIM_VECTOR_UMTS_XRES_MAX_SIZE) {
+ if (xres_vp->vp_length > AKA_SIM_VECTOR_UMTS_XRES_MAX_SIZE) {
REDEBUG("&control:%s incorrect length. Expected < "
STRINGIFY(EAP_AKA_XRES_MAX_SIZE) " bytes, got %zu bytes",
- attr_eap_aka_xres->name, xres_vp->vp_length);
+ attr_eap_aka_sim_xres->name, xres_vp->vp_length);
return -1;
}
/*
* Fetch (optional) AK
*/
- ak_vp = fr_pair_find_by_da(vps, attr_eap_aka_ak, TAG_ANY);
+ ak_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_ak, TAG_ANY);
if (ak_vp && (ak_vp->vp_length != MILENAGE_AK_SIZE)) {
REDEBUG("&control:%s incorrect length. Expected "
STRINGIFY(MILENAGE_AK_SIZE) " bytes, got %zu bytes",
- attr_eap_aka_ak->name, ak_vp->vp_length);
+ attr_eap_aka_sim_ak->name, ak_vp->vp_length);
return -1;
}
memset(keys->umts.vector.ak, 0, sizeof(keys->umts.vector.ak));
}
- memcpy(keys->umts.vector.autn, autn_vp->vp_octets, SIM_VECTOR_UMTS_AUTN_SIZE);
- memcpy(keys->umts.vector.ck, ck_vp->vp_octets, SIM_VECTOR_UMTS_CK_SIZE);
- memcpy(keys->umts.vector.ik, ik_vp->vp_octets, SIM_VECTOR_UMTS_IK_SIZE);
- memcpy(keys->umts.vector.rand, rand_vp->vp_octets, SIM_VECTOR_UMTS_RAND_SIZE);
+ memcpy(keys->umts.vector.autn, autn_vp->vp_octets, AKA_SIM_VECTOR_UMTS_AUTN_SIZE);
+ memcpy(keys->umts.vector.ck, ck_vp->vp_octets, AKA_SIM_VECTOR_UMTS_CK_SIZE);
+ memcpy(keys->umts.vector.ik, ik_vp->vp_octets, AKA_SIM_VECTOR_UMTS_IK_SIZE);
+ memcpy(keys->umts.vector.rand, rand_vp->vp_octets, AKA_SIM_VECTOR_UMTS_RAND_SIZE);
memcpy(keys->umts.vector.xres, xres_vp->vp_octets, xres_vp->vp_length);
keys->umts.vector.xres_len = xres_vp->vp_length; /* xres is variable length */
- keys->vector_src = SIM_VECTOR_SRC_QUINTUPLETS;
+ keys->vector_src = AKA_SIM_VECTOR_SRC_QUINTUPLETS;
return 0;
}
* - 0 Vector was retrieved OK and written to the specified index.
* - -1 Error retrieving vector from the specified src.
*/
-int fr_sim_vector_umts_from_attrs(REQUEST *request, VALUE_PAIR *vps,
- fr_sim_keys_t *keys, fr_sim_vector_src_t *src)
+int fr_aka_sim_vector_umts_from_attrs(REQUEST *request, VALUE_PAIR *vps,
+ fr_aka_sim_keys_t *keys, fr_aka_sim_vector_src_t *src)
{
int ret;
- rad_assert((keys->vector_type == SIM_VECTOR_NONE) || (keys->vector_type == SIM_VECTOR_UMTS));
+ rad_assert((keys->vector_type == AKA_SIM_VECTOR_NONE) || (keys->vector_type == AKA_SIM_VECTOR_UMTS));
switch (*src) {
default:
- case SIM_VECTOR_SRC_KI:
+ case AKA_SIM_VECTOR_SRC_KI:
ret = vector_umts_from_ki(request, vps, keys);
if (ret == 0) {
- *src = SIM_VECTOR_SRC_KI;
+ *src = AKA_SIM_VECTOR_SRC_KI;
break;
}
if (ret < 0) return -1;
- if (*src != SIM_VECTOR_SRC_AUTO) return 1;
+ if (*src != AKA_SIM_VECTOR_SRC_AUTO) return 1;
/* FALL-THROUGH */
- case SIM_VECTOR_SRC_QUINTUPLETS:
+ case AKA_SIM_VECTOR_SRC_QUINTUPLETS:
ret = vector_umts_from_quintuplets(request, vps, keys);
if (ret == 0) {
- *src = SIM_VECTOR_SRC_QUINTUPLETS;
+ *src = AKA_SIM_VECTOR_SRC_QUINTUPLETS;
break;;
}
if (ret < 0) return -1;
/*
* Don't change colon indent, matches other messages later...
*/
- RHEXDUMP_INLINE2(
- keys->umts.vector.autn, SIM_VECTOR_UMTS_AUTN_SIZE,
- "AUTN :");
- RHEXDUMP_INLINE2(
- keys->umts.vector.ck, SIM_VECTOR_UMTS_CK_SIZE,
- "CK :");
- RHEXDUMP_INLINE2(
- keys->umts.vector.ik, SIM_VECTOR_UMTS_IK_SIZE,
- "IK :");
- RHEXDUMP_INLINE2(
- keys->umts.vector.rand, SIM_VECTOR_UMTS_RAND_SIZE,
- "RAND :");
- RHEXDUMP_INLINE2(
- keys->umts.vector.xres, keys->umts.vector.xres_len,
- "XRES :");
+ RHEXDUMP_INLINE2(keys->umts.vector.autn, AKA_SIM_VECTOR_UMTS_AUTN_SIZE,
+ "AUTN :");
+ RHEXDUMP_INLINE2(keys->umts.vector.ck, AKA_SIM_VECTOR_UMTS_CK_SIZE,
+ "CK :");
+ RHEXDUMP_INLINE2(keys->umts.vector.ik, AKA_SIM_VECTOR_UMTS_IK_SIZE,
+ "IK :");
+ RHEXDUMP_INLINE2(keys->umts.vector.rand, AKA_SIM_VECTOR_UMTS_RAND_SIZE,
+ "RAND :");
+ RHEXDUMP_INLINE2(keys->umts.vector.xres, keys->umts.vector.xres_len,
+ "XRES :");
REXDENT();
}
- keys->vector_type = SIM_VECTOR_UMTS;
+ keys->vector_type = AKA_SIM_VECTOR_UMTS;
return 0;
}
-/** Populate a fr_sim_keys_t structure from attributes in the session-state list
+/** Populate a fr_aka_sim_keys_t structure from attributes in the session-state list
*
* @param[in] request The current request.
* @param[in] vps Session-state list
* - 0 on success.
* - -1 on validation failure.
*/
-int fr_sim_vector_umts_reauth_from_attrs(REQUEST *request, VALUE_PAIR *vps, fr_sim_keys_t *keys)
+int fr_aka_sim_vector_gsm_umts_kdf_0_reauth_from_attrs(REQUEST *request, VALUE_PAIR *vps, fr_aka_sim_keys_t *keys)
{
VALUE_PAIR *counter_vp;
VALUE_PAIR *mk_vp;
* This is the *old* counter value increment
* by 1 to get the *new* counter value
*/
- counter_vp = fr_pair_find_by_da(vps, attr_eap_aka_counter, TAG_ANY);
+ counter_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_counter, TAG_ANY);
if (!counter_vp) {
RDEBUG2("No &session-state:%s attribute found, can't calculate re-auth keys",
- attr_eap_aka_counter->name);
+ attr_eap_aka_sim_counter->name);
return 1;
}
counter_vp->vp_uint16++;
- mk_vp = fr_pair_find_by_da(vps, attr_eap_aka_mk, TAG_ANY);
+ mk_vp = fr_pair_find_by_da(vps, attr_session_data, TAG_ANY);
+ if (!mk_vp) mk_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_mk, TAG_ANY);
if (!mk_vp) {
+ RDEBUG2("Neither &session-state:%s or &session-state:%s attributes found, "
+ "can't calculate re-auth keys", attr_session_data->name, attr_eap_aka_sim_mk->name);
+ return 1;
+ }
+
+ if (mk_vp->vp_length != AKA_SIM_MK_SIZE) {
+ REDEBUG("&session-state:%s incorrect length. Expected "
+ STRINGIFY(AKA_SIM_MK_SIZE) " bytes, got %zu bytes",
+ attr_eap_aka_sim_mk->name, mk_vp->vp_length);
+ return -1;
+ }
+
+ fr_aka_sim_crypto_keys_init_kdf_0_reauth(keys, mk_vp->vp_octets, counter_vp->vp_uint16);
+
+ keys->vector_type = AKA_SIM_VECTOR_UMTS_REAUTH_KDF_0_REAUTH; /* Didn't come from a vector */
+ keys->vector_src = AKA_SIM_VECTOR_SRC_REAUTH;
+
+ return 0;
+}
+
+/** Populate a fr_aka_sim_keys_t structure from attributes in the session-state list
+ *
+ * @param[in] request The current request.
+ * @param[in] vps Session-state list
+ * @param[in] keys key structure to populate.
+ * @return
+ * - 1 if we do not have sufficient data.
+ * - 0 on success.
+ * - -1 on validation failure.
+ */
+int fr_aka_sim_vector_umts_kdf_1_reauth_from_attrs(REQUEST *request, VALUE_PAIR *vps, fr_aka_sim_keys_t *keys)
+{
+ VALUE_PAIR *counter_vp;
+ VALUE_PAIR *k_re_vp;
+
+ /*
+ * This is the *old* counter value increment
+ * by 1 to get the *new* counter value
+ */
+ counter_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_counter, TAG_ANY);
+ if (!counter_vp) {
RDEBUG2("No &session-state:%s attribute found, can't calculate re-auth keys",
- attr_eap_aka_mk->name);
+ attr_eap_aka_sim_counter->name);
return 1;
}
+ counter_vp->vp_uint16++;
- if (mk_vp->vp_length != SIM_MK_SIZE) {
- REDEBUG("&control:%s incorrect length. Expected "
- STRINGIFY(SIM_MK_SIZE) " bytes, got %zu bytes",
- attr_eap_aka_mk->name, mk_vp->vp_length);
+ k_re_vp = fr_pair_find_by_da(vps, attr_session_data, TAG_ANY);
+ if (!k_re_vp) k_re_vp = fr_pair_find_by_da(vps, attr_eap_aka_sim_k_re, TAG_ANY);
+ if (!k_re_vp) {
+ RDEBUG2("Neither &session-state:%s or &session-sate:%s attributes found, "
+ "can't calculate re-auth keys", attr_session_data->name, attr_eap_aka_sim_k_re->name);
+ return 1;
+ }
+
+ if (k_re_vp->vp_length != AKA_SIM_K_RE_SIZE) {
+ REDEBUG("&session-state:%s incorrect length. Expected "
+ STRINGIFY(AKA_SIM_K_RE_SIZE) " bytes, got %zu bytes",
+ attr_eap_aka_sim_mk->name, k_re_vp->vp_length);
return -1;
}
- fr_sim_crypto_keys_init_reauth(keys, mk_vp->vp_octets, counter_vp->vp_uint16);
+ fr_aka_sim_crypto_keys_init_umts_kdf_1_reauth(keys, k_re_vp->vp_octets, counter_vp->vp_uint16);
- keys->vector_type = SIM_VECTOR_UMTS_REAUTH; /* Didn't come from a vector */
- keys->vector_src = SIM_VECTOR_SRC_REAUTH;
+ keys->vector_type = AKA_SIM_VECTOR_UMTS_REAUTH_KDF_1_REAUTH; /* Didn't come from a vector */
+ keys->vector_src = AKA_SIM_VECTOR_SRC_REAUTH;
return 0;
}
*
* @param[in] keys key structure to clear.
*/
-void fr_sim_vector_umts_reauth_clear(fr_sim_keys_t *keys)
+void fr_aka_sim_vector_umts_reauth_clear(fr_aka_sim_keys_t *keys)
{
memset(&keys->reauth, 0, sizeof(keys->reauth));
keys->vector_src = 0;
* - 0 on success.
* - -1 on validation failure.
*/
-int fr_sim_umts_resync_from_attrs(uint64_t *new_sqn,
- REQUEST *request, VALUE_PAIR *auts_vp, fr_sim_keys_t *keys)
+int fr_aka_sim_umts_resync_from_attrs(uint64_t *new_sqn,
+ REQUEST *request, VALUE_PAIR *auts_vp, fr_aka_sim_keys_t *keys)
{
- if (keys->vector_src != SIM_VECTOR_SRC_KI) {
+ if (keys->vector_src != AKA_SIM_VECTOR_SRC_KI) {
RDEBUG2("Original vectors were not generated locally, cannot perform AUTS validation");
return 1;
}
if (auts_vp->vp_length != MILENAGE_AUTS_SIZE) {
REDEBUG("&control:%s incorrect length. Expected "
STRINGIFY(MILENAGE_AUTS_SIZE) " bytes, got %zu bytes",
- attr_eap_aka_auts->name, auts_vp->vp_length);
+ attr_eap_aka_sim_auts->name, auts_vp->vp_length);
return -1;
}
*/
/**
- * @file src/lib/sim/xlat.c
+ * @file src/lib/aka-sim/xlat.c
* @brief EAP-SIM/EAP-AKA identity detection, creation, and decyption.
*
* @copyright 2017 The FreeRADIUS server project
#include "base.h"
#include "attrs.h"
-static int sim_xlat_refs = 0;
+static int aka_sim_xlat_refs = 0;
/** Returns the SIM method EAP-SIM or EAP-AKA hinted at by the user identifier
*
- * %{sim_id_method:&id_attr}
+ * %{aka_sim_id_method:&id_attr}
*/
-static ssize_t sim_xlat_id_method(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
- UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
- REQUEST *request, char const *fmt)
+static ssize_t aka_sim_xlat_id_method_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+ UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
+ REQUEST *request, char const *fmt)
{
- vp_tmpl_t *vpt;
- TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
- ssize_t slen, id_len;
- char const *p = fmt, *id, *method;
- fr_sim_id_type_t type_hint;
- fr_sim_method_hint_t method_hint;
+ vp_tmpl_t *vpt;
+ TALLOC_CTX *our_ctx = talloc_init("aka_sim_xlat");
+ ssize_t slen, id_len;
+ char const *p = fmt, *id, *method;
+ fr_aka_sim_id_type_t type_hint;
+ fr_aka_sim_method_hint_t method_hint;
/*
* Trim whitespace
}
id_len = talloc_array_length(id) - 1;
- if (fr_sim_id_type(&type_hint, &method_hint, id, id_len) < 0) {
- RPEDEBUG2("SIM ID \"%pV\" has unrecognised format", fr_box_strvalue_len(id, id_len));
+ if (fr_aka_sim_id_type(&type_hint, &method_hint, id, id_len) < 0) {
+ RPEDEBUG2("AKA/SIM Id \"%pV\" has unrecognised format", fr_box_strvalue_len(id, id_len));
goto error;
}
- method = fr_dict_enum_alias_by_value(attr_sim_method_hint, fr_box_uint32(method_hint));
- if (!method) {
- REDEBUG("Missing Sim-Method-Hint value");
- goto error;
+ switch (method_hint) {
+ default:
+ case AKA_SIM_METHOD_HINT_UNKNOWN:
+ *out = NULL;
+ return 0;
+
+ case AKA_SIM_METHOD_HINT_SIM:
+ method = fr_dict_enum_alias_by_value(attr_eap_aka_sim_method_hint,
+ fr_box_uint32(FR_METHOD_HINT_VALUE_SIM));
+ break;
+
+ case AKA_SIM_METHOD_HINT_AKA:
+ method = fr_dict_enum_alias_by_value(attr_eap_aka_sim_method_hint,
+ fr_box_uint32(FR_METHOD_HINT_VALUE_AKA));
+ break;
+
+ case AKA_SIM_METHOD_HINT_AKA_PRIME:
+ method = fr_dict_enum_alias_by_value(attr_eap_aka_sim_method_hint,
+ fr_box_uint32(FR_METHOD_HINT_VALUE_AKA_PRIME));
+ break;
}
+
*out = talloc_typed_strdup(ctx, method);
talloc_free(our_ctx);
/** Returns the type of identity used
*
- * %{sim_id_type:&id_attr}
+ * %{aka_sim_id_type:&id_attr}
*/
-static ssize_t sim_xlat_id_type(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+static ssize_t aka_sim_xlat_id_type_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
REQUEST *request, char const *fmt)
{
- vp_tmpl_t *vpt;
- TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
- ssize_t slen, id_len;
- char const *p = fmt, *id, *method;
- fr_sim_id_type_t type_hint;
- fr_sim_method_hint_t method_hint;
+ vp_tmpl_t *vpt;
+ TALLOC_CTX *our_ctx = talloc_init("aka_sim_xlat");
+ ssize_t slen, id_len;
+ char const *p = fmt, *id, *type;
+ fr_aka_sim_id_type_t type_hint;
+ fr_aka_sim_method_hint_t method_hint;
/*
* Trim whitespace
}
id_len = talloc_array_length(id) - 1;
- if (fr_sim_id_type(&type_hint, &method_hint, id, id_len) < 0) {
- RPEDEBUG2("SIM ID \"%pV\" has unrecognised format", fr_box_strvalue_len(id, id_len));
+ if (fr_aka_sim_id_type(&type_hint, &method_hint, id, id_len) < 0) {
+ RPEDEBUG2("AKA/AKA/SIM Id \"%pV\" has unrecognised format", fr_box_strvalue_len(id, id_len));
goto error;
}
- method = fr_dict_enum_alias_by_value(attr_sim_identity_type, fr_box_uint32(type_hint));
- if (!method) {
- REDEBUG("Missing Sim-Method-Hint value");
- goto error;
+ switch (type_hint) {
+ default:
+ case AKA_SIM_ID_TYPE_UNKNOWN:
+ *out = NULL;
+ return 0;
+
+ case AKA_SIM_ID_TYPE_PERMANENT:
+ type = fr_dict_enum_alias_by_value(attr_eap_aka_sim_identity_type,
+ fr_box_uint32(FR_IDENTITY_TYPE_VALUE_PERMANENT));
+ break;
+
+ case AKA_SIM_ID_TYPE_PSEUDONYM:
+ type = fr_dict_enum_alias_by_value(attr_eap_aka_sim_identity_type,
+ fr_box_uint32(FR_IDENTITY_TYPE_VALUE_PSEUDONYM));
+ break;
+
+ case AKA_SIM_ID_TYPE_FASTAUTH:
+ type = fr_dict_enum_alias_by_value(attr_eap_aka_sim_identity_type,
+ fr_box_uint32(FR_IDENTITY_TYPE_VALUE_FASTAUTH));
+ break;
}
- *out = talloc_typed_strdup(ctx, method);
+
+ *out = talloc_typed_strdup(ctx, type);
talloc_free(our_ctx);
return talloc_array_length(*out) - 1;
/** Returns the key index from a 3gpp pseudonym
*
- * %{sim_id_3gpp_pseudonym_key_index:&id_attr}
+ * %{3gpp_pseudonym_key_index:&id_attr}
*
*/
-static ssize_t sim_xlat_3gpp_pseudonym_key_index(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+static ssize_t aka_sim_3gpp_pseudonym_key_index_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
REQUEST *request, char const *fmt)
{
vp_tmpl_t *vpt;
- TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
+ TALLOC_CTX *our_ctx = talloc_init("aka_sim_xlat");
ssize_t slen, id_len;
char const *p = fmt, *id;
}
id_len = talloc_array_length(id) - 1;
- if (id_len != SIM_3GPP_PSEUDONYM_LEN) {
+ if (id_len != AKA_SIM_3GPP_PSEUDONYM_LEN) {
REDEBUG2("3gpp pseudonym incorrect length, expected %i bytes, got %zu bytes",
- SIM_3GPP_PSEUDONYM_LEN, id_len);
+ AKA_SIM_3GPP_PSEUDONYM_LEN, id_len);
goto error;
}
- MEM(*out = talloc_typed_asprintf(ctx, "%i", fr_sim_id_3gpp_pseudonym_tag(id)));
+ MEM(*out = talloc_typed_asprintf(ctx, "%i", fr_aka_sim_id_3gpp_pseudonym_tag(id)));
talloc_free(our_ctx);
return talloc_array_length(*out) - 1;
}
-static ssize_t sim_xlat_3gpp_pseudonym_decrypt(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+static ssize_t aka_sim_3gpp_pseudonym_decrypt_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
REQUEST *request, char const *fmt)
{
vp_tmpl_t *id_vpt, *key_vpt;
- TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
+ TALLOC_CTX *our_ctx = talloc_init("aka_sim_xlat");
ssize_t slen, id_len, key_len;
uint8_t tag;
char out_tag;
uint8_t *key;
- char decrypted[SIM_IMSI_MAX_LEN + 1];
+ char decrypted[AKA_SIM_IMSI_MAX_LEN + 1];
char const *p = fmt, *id;
/*
}
id_len = talloc_array_length(id);
- if (id_len != (SIM_3GPP_PSEUDONYM_LEN + 1)) {
+ if (id_len != (AKA_SIM_3GPP_PSEUDONYM_LEN + 1)) {
REDEBUG2("3gpp pseudonym incorrect length, expected %i bytes, got %zu bytes",
- SIM_3GPP_PSEUDONYM_LEN + 1, id_len);
+ AKA_SIM_3GPP_PSEUDONYM_LEN + 1, id_len);
goto error;
}
goto error;
}
- tag = fr_sim_id_3gpp_pseudonym_tag(id);
+ tag = fr_aka_sim_id_3gpp_pseudonym_tag(id);
switch (tag) {
- case SIM_ID_TAG_PSEUDONYM_SIM_B64:
- out_tag = SIM_ID_TAG_PERMANENT_SIM;
+ case ID_TAG_SIM_PSEUDONYM_B64:
+ out_tag = ID_TAG_SIM_PERMANENT;
break;
- case SIM_ID_TAG_PSEUDONYM_AKA_B64:
- out_tag = SIM_ID_TAG_PERMANENT_AKA;
+ case ID_TAG_AKA_PSEUDONYM_B64:
+ out_tag = ID_TAG_AKA_PERMANENT;
break;
- case SIM_ID_TAG_PSEUDONYM_AKA_PRIME_B64:
- out_tag = SIM_ID_TAG_PERMANENT_AKA_PRIME;
+ case ID_TAG_AKA_PRIME_PSEUDONYM_B64:
+ out_tag = ID_TAG_AKA_PRIME_PERMANENT;
break;
default:
- REDEBUG2("Unexpected tag value (%u) in SIM ID \"%pV\"", tag, fr_box_strvalue_len(id, id_len));
+ REDEBUG2("Unexpected tag value (%u) in AKA/SIM Id \"%pV\"", tag, fr_box_strvalue_len(id, id_len));
goto error;
}
RDEBUG2("Decrypting \"%pV\"", fr_box_strvalue_len(id, id_len));
- if (fr_sim_id_3gpp_pseudonym_decrypt(decrypted, id, key) < 0) {
- RPEDEBUG2("Failed decrypting SIM ID");
+ if (fr_aka_sim_id_3gpp_pseudonym_decrypt(decrypted, id, key) < 0) {
+ RPEDEBUG2("Failed decrypting AKA/SIM Id");
goto error;
}
/** Encrypts a 3gpp pseudonym
*
- * %{sim_id_3gpp_pseudonym_encrypt:&id_attr &key_attr key_index}
+ * %{3gpp_pseudonym_encrypt:&id_attr &key_attr key_index}
*
*/
-static ssize_t sim_xlat_3gpp_pseudonym_encrypt(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
+static ssize_t aka_sim_3gpp_pseudonym_encrypt_xlat(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
REQUEST *request, char const *fmt)
{
- vp_tmpl_t *id_vpt, *key_vpt;
- TALLOC_CTX *our_ctx = talloc_init("sim_xlat");
- ssize_t slen, id_len, key_len;
- uint8_t *key, tag = 0;
- unsigned long key_index;
- char encrypted[SIM_3GPP_PSEUDONYM_LEN + 1];
- char const *p = fmt, *id;
+ vp_tmpl_t *id_vpt, *key_vpt;
+ TALLOC_CTX *our_ctx = talloc_init("aka_sim_xlat");
+ ssize_t slen, id_len, key_len;
+ uint8_t *key, tag = 0;
+ unsigned long key_index;
+ char encrypted[AKA_SIM_3GPP_PSEUDONYM_LEN + 1];
+ char const *p = fmt, *id;
char const *id_p, *id_end;
- fr_sim_id_type_t type_hint;
- fr_sim_method_hint_t method_hint;
+ fr_aka_sim_id_type_t type_hint;
+ fr_aka_sim_method_hint_t method_hint;
/*
* Trim whitespace
* Determine what type/method hints are in the current ID.
*/
id_len = talloc_array_length(id) - 1;
- if (id_len == (SIM_IMSI_MAX_LEN + 1)) { /* +1 for ID tag */
- if (fr_sim_id_type(&type_hint, &method_hint, id, id_len) < 0) {
+ if (id_len == (AKA_SIM_IMSI_MAX_LEN + 1)) { /* +1 for ID tag */
+ if (fr_aka_sim_id_type(&type_hint, &method_hint, id, id_len) < 0) {
RPEDEBUG2("SIM ID \"%pV\" has unrecognised format", fr_box_strvalue_len(id, id_len));
goto error;
}
- if (type_hint != SIM_ID_TYPE_PERMANENT) {
+ if (type_hint != AKA_SIM_ID_TYPE_PERMANENT) {
REDEBUG2("SIM ID \"%pV\" is not a permanent identity (IMSI)", fr_box_strvalue_len(id, id_len));
goto error;
}
switch (method_hint) {
- case SIM_METHOD_HINT_SIM:
- tag = SIM_ID_TAG_PSEUDONYM_SIM_B64;
+ case AKA_SIM_METHOD_HINT_SIM:
+ tag = ID_TAG_SIM_PSEUDONYM_B64;
break;
- case SIM_METHOD_HINT_AKA:
- tag = SIM_ID_TAG_PSEUDONYM_AKA_B64;
+ case AKA_SIM_METHOD_HINT_AKA:
+ tag = ID_TAG_AKA_PSEUDONYM_B64;
break;
- case SIM_METHOD_HINT_AKA_PRIME:
- tag = SIM_ID_TAG_PSEUDONYM_AKA_PRIME_B64;
+ case AKA_SIM_METHOD_HINT_AKA_PRIME:
+ tag = ID_TAG_AKA_PRIME_PSEUDONYM_B64;
break;
- case SIM_METHOD_HINT_UNKNOWN:
- REDEBUG2("SIM ID \"%pV\" does not contain a method hint", fr_box_strvalue_len(id, id_len));
+ case AKA_SIM_METHOD_HINT_UNKNOWN:
+ case AKA_SIM_METHOD_HINT_MAX:
+ REDEBUG2("AKA/SIM ID \"%pV\" does not contain a method hint", fr_box_strvalue_len(id, id_len));
goto error;
}
/*
* ID lacks a hint byte, figure it out from &control:EAP-Type
*/
- } else if ((id_len >= SIM_IMSI_MIN_LEN) && (id_len <= SIM_IMSI_MAX_LEN)) {
+ } else if ((id_len >= AKA_SIM_IMSI_MIN_LEN) && (id_len <= AKA_SIM_IMSI_MAX_LEN)) {
VALUE_PAIR *eap_type;
- eap_type = fr_pair_find_by_da(request->control, attr_eap_type, TAG_ANY);
+ eap_type = fr_pair_find_by_da(request->packet->vps, attr_eap_type, TAG_ANY);
if (!eap_type) {
REDEBUG("SIM ID does not contain method hint, and no &control:EAP-Type found. "
"Don't know what tag to prepend to encrypted identity");
switch (eap_type->vp_uint32) {
case FR_EAP_TYPE_VALUE_SIM:
- tag = SIM_ID_TAG_PSEUDONYM_SIM_B64;
+ tag = ID_TAG_SIM_PSEUDONYM_B64;
break;
case FR_EAP_TYPE_VALUE_AKA:
- tag = SIM_ID_TAG_PSEUDONYM_AKA_B64;
+ tag = ID_TAG_AKA_PSEUDONYM_B64;
break;
case FR_EAP_TYPE_VALUE_AKA_PRIME:
- tag = SIM_ID_TAG_PSEUDONYM_AKA_PRIME_B64;
+ tag = ID_TAG_AKA_PRIME_PSEUDONYM_B64;
break;
default:
id_p = id;
id_end = id_p + id_len;
} else {
- REDEBUG2("IMSI incorrect length, expected %i bytes, got %zu bytes", SIM_IMSI_MAX_LEN + 1,
+ REDEBUG2("IMSI incorrect length, expected %i bytes, got %zu bytes", AKA_SIM_IMSI_MAX_LEN + 1,
id_len);
goto error;
*
* Strip existing tag from the permanent id
*/
- if (fr_sim_id_3gpp_pseudonym_encrypt(encrypted, id_p, id_end - id_p, tag, (uint8_t)key_index, key) < 0) {
+ if (fr_aka_sim_id_3gpp_pseudonym_encrypt(encrypted, id_p, id_end - id_p, tag, (uint8_t)key_index, key) < 0) {
RPEDEBUG2("Failed encrypting SIM ID \"%pV\"", fr_box_strvalue_len(id, id_len));
return -1;
}
return talloc_array_length(*out) - 1;
}
-void sim_xlat_register(void)
+void fr_aka_sim_xlat_register(void)
{
- if (sim_xlat_refs) {
- sim_xlat_refs++;
+ if (aka_sim_xlat_refs) {
+ aka_sim_xlat_refs++;
return;
}
- xlat_register(NULL, "sim_id_method", sim_xlat_id_method, NULL, NULL, 0, 0, true);
- xlat_register(NULL, "sim_id_type", sim_xlat_id_type, NULL, NULL, 0, 0, true);
+ xlat_register(NULL, "aka_sim_id_method", aka_sim_xlat_id_method_xlat, NULL, NULL, 0, 0, true);
+ xlat_register(NULL, "aka_sim_id_type", aka_sim_xlat_id_type_xlat, NULL, NULL, 0, 0, true);
xlat_register(NULL, "3gpp_pseudonym_key_index",
- sim_xlat_3gpp_pseudonym_key_index, NULL, NULL, 0, 0, true);
+ aka_sim_3gpp_pseudonym_key_index_xlat, NULL, NULL, 0, 0, true);
xlat_register(NULL, "3gpp_pseudonym_decrypt",
- sim_xlat_3gpp_pseudonym_decrypt, NULL, NULL, 0, 0, true);
+ aka_sim_3gpp_pseudonym_decrypt_xlat, NULL, NULL, 0, 0, true);
xlat_register(NULL, "3gpp_pseudonym_encrypt",
- sim_xlat_3gpp_pseudonym_encrypt, NULL, NULL, 0, 0, true);
- sim_xlat_refs = 1;
+ aka_sim_3gpp_pseudonym_encrypt_xlat, NULL, NULL, 0, 0, true);
+ aka_sim_xlat_refs = 1;
}
-void sim_xlat_unregister(void)
+void fr_aka_sim_xlat_unregister(void)
{
- if (sim_xlat_refs > 1) {
- sim_xlat_refs--;
+ if (aka_sim_xlat_refs > 1) {
+ aka_sim_xlat_refs--;
return;
}
- xlat_unregister("sim_id_method");
- xlat_unregister("sim_id_type");
+ xlat_unregister("aka_sim_id_method");
+ xlat_unregister("aka_sim_id_type");
xlat_unregister("3gpp_pseudonym_key_index");
xlat_unregister("3gpp_pseudonym_decrypt");
xlat_unregister("3gpp_pseudonym_encrypt");
- sim_xlat_refs = 0;
+ aka_sim_xlat_refs = 0;
}
ifneq "$(OPENSSL_LIBS)" ""
-TARGET := libfreeradius-eap-sim.a
+TARGET := libfreeradius-sim.a
endif
SOURCES := \
- base.c \
comp128.c \
- crypto.c \
- decode.c \
- encode.c \
- fips186prf.c \
- id.c \
milenage.c \
- vector.c \
- xlat.c
+ ts_34_108.c
-SRC_INCDIRS := . ${top_srcdir}/src/modules/rlm_eap/lib/base ${top_srcdir}/src/modules/rlm_eap/
-TGT_PREREQS := $(LIBFREERADIUS_SERVER) libfreeradius-util.a
+TGT_PREREQS := libfreeradius-util.a
#pragma once
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* $Id$
- * @file src/lib/sim/base.h
- * @brief Functions common to SIM protocols (EAP-SIM/EAP-AKA/EAP-AKA')
*
- * The development of the EAP/SIM support was funded by Internet Foundation
- * Austria (http://www.nic.at/ipa).
+ * @file src/lib/base.h
+ * @brief Master include file for all lib/sim functions.
*
- * @copyright 2003 Michael Richardson (mcr@sandelman.ottawa.on.ca)
- * @copyright 2003-2016 The FreeRADIUS server project
+ * @copyright 2019 The FreeRADIUS server project
*/
-RCSIDH(sim_h, "$Id$")
+RCSIDH(sim_base_h, "$Id$")
-#include <assert.h>
-#include <openssl/evp.h>
-#include <freeradius-devel/eap/compose.h>
-#include <freeradius-devel/eap/types.h>
-
-#include "id.h"
-#include "eap_sim_common.h"
-#include "eap_aka_common.h"
-
-#define SIM_MAX_STRING_LENGTH 1016 //!< Maximum size of a SIM/AKA['] string ((4 * 255) - 4).
-#define SIM_IV_SIZE 16 //!< Length of the IV used when processing AT_ENCR.
-#define SIM_MAC_DIGEST_SIZE 16 //!< Length of MAC used to prevent packet modification.
-#define SIM_MAC_SIZE 20 //!< Length of MAC used to prevent packet modification.
-#define SIM_AUTH_SIZE 16
-#define SIM_SQN_AK_SIZE 6
-#define SIM_NONCE_S_SIZE 16 //!< Length of re-authentication nonce
-
-#define SIM_KI_SIZE 16 //!< Length of secret data shared between SIM and AuC.
-#define SIM_OP_SIZE 16 //!< Length of Operator Algorithm Configuration.
-#define SIM_OPC_SIZE 16 //!< Length of modified Operator Algorithm Configuration.
-
-#define SIM_MK_SIZE 20 //!< Master key size
-
-#define SIM_SKIPPABLE_MAX 127 //!< The last non-skippable attribute.
-
-#define SIM_VECTOR_GSM_RAND_SIZE 16 //!< Length of RAND in GSM triplet.
-#define SIM_VECTOR_GSM_SRES_SIZE 4 //!< Length of SRES in GSM triplet.
-#define SIM_VECTOR_GSM_KC_SIZE 8 //!< Length of Kc in GSM triplet.
-
-#define SIM_VECTOR_UMTS_AUTN_SIZE 16
-#define SIM_VECTOR_UMTS_CK_SIZE 16
-#define SIM_VECTOR_UMTS_IK_SIZE 16
-#define SIM_VECTOR_UMTS_AK_SIZE 6
-#define SIM_VECTOR_UMTS_RAND_SIZE 16
-#define SIM_VECTOR_UMTS_XRES_MAX_SIZE 16
-#define SIM_VECTOR_UMTS_RES_MAX_SIZE 16
-
-/** The type of auth vector held by a fr_sim_keys_t
- */
-typedef enum {
- SIM_VECTOR_NONE = 0,
- SIM_VECTOR_GSM, //!< Vector is GSM triplets.
- SIM_VECTOR_UMTS, //!< Vector is UMTS quintuplets.
- SIM_VECTOR_UMTS_REAUTH
-} fr_sim_vector_type_t;
-
-/** Where to get EAP-SIM vectors from
- */
-typedef enum {
- SIM_VECTOR_SRC_AUTO, //!< Discover where to get Triplets from automatically.
- SIM_VECTOR_SRC_TRIPLETS, //!< Source of triplets is EAP-SIM-* attributes.
- SIM_VECTOR_SRC_QUINTUPLETS, //!< Source of triplets is derived from EAP-AKA-*
- ///< quintuplets.
- SIM_VECTOR_SRC_KI, //!< Should generate triplets locally using a Ki.
- SIM_VECTOR_SRC_REAUTH
-} fr_sim_vector_src_t;
-
-typedef struct {
- uint8_t rand[SIM_VECTOR_GSM_RAND_SIZE]; //!< RAND challenge to the SIM.
- union {
- uint8_t sres[SIM_VECTOR_GSM_SRES_SIZE]; //!< Signing response.
- uint32_t sres_uint32;
- };
-
- union {
- uint8_t kc[SIM_VECTOR_GSM_KC_SIZE]; //!< Keying response.
- uint64_t kc_uint64;
- };
-} fr_sim_vector_gsm_t;
-
-typedef struct {
- uint8_t autn[SIM_VECTOR_UMTS_AUTN_SIZE]; //!< Authentication vector from the AuC.
- uint8_t ck[SIM_VECTOR_UMTS_CK_SIZE]; //!< Ciphering key.
- uint8_t ik[SIM_VECTOR_UMTS_IK_SIZE]; //!< Integrity key.
- uint8_t ak[SIM_VECTOR_UMTS_AK_SIZE]; //!< Anonymity key.
- uint8_t rand[SIM_VECTOR_UMTS_RAND_SIZE]; //!< RAND challenge to the SIM.
- uint8_t xres[SIM_VECTOR_UMTS_RES_MAX_SIZE]; //!< Signing response.
- size_t xres_len; //!< Length of res (it's variable).
-} fr_sim_vector_umts_t;
-
-/** Stores our checkcode state
- *
- * The checkcode is a hash of all identity packets exchanged
- * up until the challenge is sent.
- *
- * It allows both parties to verify that they've seen the same
- * sequence of packets.
- */
-typedef struct {
- EVP_MD_CTX *md_ctx; //!< Context to hold state of digest as we
- ///< consume packets.
-} fr_sim_checkcode_t;
-
-/** Master key state struct for all SIMlike EAP protocols
- *
- */
-typedef struct {
- /*
- * Inputs
- */
- uint8_t *identity; //!< Identity from AT_IDENTITY.
- size_t identity_len; //!< Length of the identity.
-
- uint8_t *network; //!< Network name (EAP-AKA-Prime only).
- size_t network_len; //!< Length of the network name (EAP-AKA-Prime only).
-
- uint64_t sqn; //!< Sequence number
-
- struct {
- /*
- * Authentication vectors from HLR or local AuC
- */
- union {
- /** Input to kdf_0_gsm
- */
- struct {
- fr_sim_vector_gsm_t vector[3]; //!< GSM vectors.
- uint32_t num_vectors; //!< Number of input vectors
- //!< we're using (2 or 3).
-
- uint8_t nonce_mt[EAP_SIM_NONCE_MT_SIZE];//!< Nonce provided by the client.
- uint8_t version_list[FR_MAX_STRING_LEN];//!< Version list from negotiation.
- uint8_t version_list_len; //!< Length of version list.
- uint8_t version_select[2]; //!< Version we agreed.
- } gsm;
-
- /** Input to kdf_*_umts
- */
- struct {
- fr_sim_vector_umts_t vector; //!< UMTS vector.
- uint16_t kdf_selected;
- } umts;
- };
-
- /*
- * Original ki and OPc to allow AUTS validation
- */
- struct {
- uint8_t ki[SIM_KI_SIZE]; //!< Secret shared between AuC and SIM.
- uint8_t opc[SIM_OPC_SIZE]; //!< Operator algorithm input.
- } auc;
-
- /*
- * Re-authentication data
- */
- struct {
- uint16_t counter; //!< Re-authentication counter.
- uint8_t nonce_s[SIM_NONCE_S_SIZE]; //!< Re-authentication challenge.
- } reauth;
-
-
- fr_sim_vector_type_t vector_type; //!< What type of authentication vector
- //!< we're using to authenticate the SIM.
- fr_sim_vector_src_t vector_src; //!< Where the vector came from.
- };
-
- /*
- * Intermediates
- */
- uint8_t ck_prime[SIM_VECTOR_UMTS_CK_SIZE]; //!< Derived from CK, for AKA'.
- uint8_t ik_prime[SIM_VECTOR_UMTS_IK_SIZE]; //!< Derived from IK, for AKA'.
-
- /*
- * Inputs/outputs
- */
- uint8_t master_key[SIM_MK_SIZE]; //!< Master key from session attributes.
-
- /*
- * Outputs
- */
- uint8_t k_aut[32]; //!< Derived authentication key.
- size_t k_aut_len; //!< Length of k_aut. 16 for AKA/SIM, 32 for AKA'.
- uint8_t k_re[32]; //!< Derived reauthentication key.
- uint8_t k_encr[16]; //!< Derived encryption key.
-
- uint8_t msk[64]; //!< Derived master session key.
- uint8_t emsk[64]; //!< Derived extended master session key.
-} fr_sim_keys_t;
-
-typedef struct {
- fr_sim_keys_t const *keys; //!< From the EAP session.
- uint8_t iv[SIM_IV_SIZE]; //!< From the current packet.
- bool have_iv; //!< Whether we found the IV already.
-} fr_sim_decode_ctx_t;
-
-typedef struct {
- fr_dict_attr_t const *root; //!< Root attribute of the dictionary.
- fr_sim_keys_t const *keys; //!< From the EAP session.
- uint8_t iv[SIM_IV_SIZE]; //!< Generated by us using our PRNG.
- bool iv_included; //!< Whether we've already added an IV to this packet.
-
- /*
- * Additional HMAC inputs
- */
- EVP_MD const *hmac_md; //!< HMAC digest algorithm, usually EVP_sha1().
- eap_packet_t *eap_packet; //!< Needed for HMAC generation so we can construct
- ///< the EAP packet header.
- uint8_t const *hmac_extra; //!< Extra data for the HMAC function.
- size_t hmac_extra_len; //!< The length of the HMAC data.
-} fr_sim_encode_ctx_t;
-
-extern size_t const fr_sim_attr_sizes[FR_TYPE_MAX + 1][2];
-
-/*
- * decode.c
- */
-ssize_t fr_sim_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dict_t const *dict,
- uint8_t const *data, size_t data_len, void *decoder_ctx);
-
-int fr_sim_decode(REQUEST *request, fr_cursor_t *decoded, fr_dict_t const *dict,
- uint8_t const *data, size_t data_len, fr_sim_decode_ctx_t *ctx);
-
-/*
- * encode.c
- */
-ssize_t fr_sim_encode_pair(uint8_t *out, size_t outlen, fr_cursor_t *cursor, void *encoder_ctx);
-
-ssize_t fr_sim_encode(REQUEST *request, VALUE_PAIR *to_encode, void *encode_ctx);
-
-/*
- * base.c
- */
-size_t fr_sim_attr_len(VALUE_PAIR const *vp);
-
-size_t fr_sim_octets_prefix_len(fr_dict_attr_t const *da);
-
-int fr_sim_init(void);
-
-void fr_sim_free(void);
-
-/*
- * crypto.c
- */
-int fr_sim_crypto_init_checkcode(TALLOC_CTX *ctx, fr_sim_checkcode_t **checkcode, EVP_MD const *md);
-
-int fr_sim_crypto_update_checkcode(fr_sim_checkcode_t *checkcode, eap_packet_t *eap_packet);
-
-ssize_t fr_sim_crypto_finalise_checkcode(uint8_t *out, fr_sim_checkcode_t **checkcode);
-
-ssize_t fr_sim_crypto_sign_packet(uint8_t out[static SIM_MAC_DIGEST_SIZE],
- eap_packet_t *eap_packet, bool zero_mac,
- EVP_MD const *md, uint8_t const *key, size_t const key_len,
- uint8_t const *hmac_extra, size_t const hmac_extra_len);
-
-int fr_sim_crypto_kdf_0_gsm(fr_sim_keys_t *keys);
-
-int fr_sim_crypto_kdf_0_umts(fr_sim_keys_t *keys);
-
-int fr_sim_crypto_kdf_1_umts(fr_sim_keys_t *keys);
-
-void fr_sim_crypto_keys_init_reauth(fr_sim_keys_t *keys,
- uint8_t const master_key[static SIM_MK_SIZE], uint16_t counter);
-
-int fr_sim_crypto_kdf_0_reauth(fr_sim_keys_t *keys);
-
-int fr_sim_crypto_kdf_1_reauth(fr_sim_keys_t *keys);
-
-void fr_sim_crypto_keys_log(REQUEST *request, fr_sim_keys_t *keys);
-
-/*
- * vector.c
- */
-int fr_sim_vector_gsm_from_attrs(eap_session_t *eap_session, VALUE_PAIR *vps,
- int idx, fr_sim_keys_t *keys, fr_sim_vector_src_t *src);
-
-int fr_sim_vector_umts_from_attrs(REQUEST *request, VALUE_PAIR *vps,
- fr_sim_keys_t *keys, fr_sim_vector_src_t *src);
-
-int fr_sim_vector_umts_reauth_from_attrs(REQUEST *request, VALUE_PAIR *vps,
- fr_sim_keys_t *keys);
-
-void fr_sim_vector_umts_reauth_clear(fr_sim_keys_t *keys);
-
-int fr_sim_umts_resync_from_attrs(uint64_t *new_sqn,
- REQUEST *request, VALUE_PAIR *auts_vp, fr_sim_keys_t *keys);
-
-/*
- * fips186prf.c
- */
-void fr_sim_fips186_2prf(uint8_t out[static 160], uint8_t mk[static 20]);
-
-/*
- * xlat.c
- */
-void sim_xlat_register(void);
-void sim_xlat_unregister(void);
+#include <freeradius-devel/sim/common.h>
+#include <freeradius-devel/sim/comp128.h
+#include <freeradius-devel/sim/milenage.h>
+#include <freeradius-devel/sim/ts_34_108.h>
--- /dev/null
+#pragma once
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file lib/sim/common.h
+ * @brief Common code used by multiple SIM algorithms
+ *
+ * @copyright 2019 The FreeRADIUS server project
+ * @copyright 2019 Arran Cudbard-Bell \<a.cudbardb@freeradius.org\>
+ */
+
+/** Copy a 48bit value from a 64bit integer into a uint8_t buff in big endian byte order
+ *
+ * There may be fast ways of doing this, but this is the *correct*
+ * way, and does not make assumptions about how integers are laid
+ * out in memory.
+ *
+ * @param[out] out 6 byte butter to store value.
+ * @param[in] i integer value.
+ * @return pointer to out.
+ */
+static inline uint8_t *uint48_to_buff(uint8_t out[static 6], uint64_t i)
+{
+ out[0] = (i & 0xff0000000000) >> 40;
+ out[1] = (i & 0x00ff00000000) >> 32;
+ out[2] = (i & 0x0000ff000000) >> 24;
+ out[3] = (i & 0x000000ff0000) >> 16;
+ out[4] = (i & 0x00000000ff00) >> 8;
+ out[5] = (i & 0x0000000000ff);
+
+ return out;
+}
+
+/** Convert a 48bit big endian value into a unsigned 64bit integer
+ *
+ */
+static inline uint64_t uint48_from_buff(uint8_t const in[6])
+{
+ uint64_t i = 0;
+
+ i |= ((uint64_t)in[0]) << 40;
+ i |= ((uint64_t)in[1]) << 32;
+ i |= ((uint32_t)in[2]) << 24;
+ i |= ((uint32_t)in[3]) << 16;
+ i |= ((uint16_t)in[4]) << 8;
+ i |= in[5];
+
+ return i;
+}
/**
- * @file src/lib/sim/milenage.c
+ * @file src/lib/aka-sim/milenage.c
* @brief 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
*
* This file implements an example authentication algorithm defined for 3GPP
#include <freeradius-devel/tls/log.h>
#include <freeradius-devel/util/proto.h>
#include <openssl/evp.h>
+#include "common.h"
#include "milenage.h"
#define MILENAGE_MAC_A_SIZE 8
#pragma once
/**
- * @file src/lib/sim/milenage.h
+ * @file src/lib/aka-sim/milenage.h
* @brief 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
*
* This file implements an example authentication algorithm defined for 3GPP
#define MILENAGE_SRES_SIZE 4
#define MILENAGE_KC_SIZE 8
-/** Copy a 48bit value from a 64bit integer into a uint8_t buff in big endian byte order
- *
- * There may be fast ways of doing this, but this is the *correct*
- * way, and does not make assumptions about how integers are laid
- * out in memory.
- *
- * @param[out] out 6 byte butter to store value.
- * @param[in] i integer value.
- * @return pointer to out.
- */
-static inline uint8_t *uint48_to_buff(uint8_t out[static 6], uint64_t i)
-{
- out[0] = (i & 0xff0000000000) >> 40;
- out[1] = (i & 0x00ff00000000) >> 32;
- out[2] = (i & 0x0000ff000000) >> 24;
- out[3] = (i & 0x000000ff0000) >> 16;
- out[4] = (i & 0x00000000ff00) >> 8;
- out[5] = (i & 0x0000000000ff);
-
- return out;
-}
-
-/** Convert a 48bit big endian value into a unsigned 64bit integer
- *
- */
-static inline uint64_t uint48_from_buff(uint8_t const in[6])
-{
- uint64_t i = 0;
-
- i |= ((uint64_t)in[0]) << 40;
- i |= ((uint64_t)in[1]) << 32;
- i |= ((uint32_t)in[2]) << 24;
- i |= ((uint32_t)in[3]) << 16;
- i |= ((uint16_t)in[4]) << 8;
- i |= in[5];
-
- return i;
-}
-
int milenage_opc_generate(uint8_t opc[MILENAGE_OPC_SIZE],
uint8_t const op[MILENAGE_OP_SIZE],
uint8_t const ki[MILENAGE_KI_SIZE]);
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file ts_34_108.h
+ * @brief Implementation of the TS.34.108 dummy USMI algorithm
+ *
+ * @copyright 2019 The FreeRADIUS server project
+ * @copyright 2019 Arran Cudbard-Bell \<a.cudbardb@freeradius.org\>
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include <freeradius-devel/util/proto.h>
+#include "common.h"
+#include "ts_34_108.h"
+
+static_assert(TS_34_108_KI_SIZE == TS_34_108_RAND_SIZE,
+ "TS.34.108 Ki and RAND sizes must be identical");
+
+#define TS_34_108_XDOUT_SIZE TS_34_108_RAND_SIZE
+#define TS_34_108_CDOUT_SIZE 8
+#define TS_34_108_XMAC_SIZE 8
+
+int ts_34_108_umts_generate(uint8_t autn[TS_34_108_AUTN_SIZE],
+ uint8_t ik[TS_34_108_IK_SIZE],
+ uint8_t ck[TS_34_108_CK_SIZE],
+ uint8_t ak[TS_34_108_AK_SIZE],
+ uint8_t res[TS_34_108_RES_SIZE],
+ uint8_t const amf[TS_34_108_AMF_SIZE],
+ uint8_t const ki[TS_34_108_KI_SIZE],
+ uint64_t sqn,
+ uint8_t const rand[TS_34_108_RAND_SIZE])
+{
+ size_t i;
+ uint8_t xdout[TS_34_108_XDOUT_SIZE];
+ uint8_t cdout[TS_34_108_CDOUT_SIZE];
+ uint8_t xmac[TS_34_108_XMAC_SIZE];
+ uint8_t sqn_buff[TS_34_108_SQN_SIZE];
+ uint8_t *p;
+
+ /*
+ * Step 1:
+ * XOR to the challenge RAND, a predefined number K (in which at least
+ * one bit is not zero, see clause 8.2), having the same bit length
+ * (128 * bits) as RAND.
+ */
+ for (i = 0; i < sizeof(xdout); i++) xdout[i] = ki[i] ^ rand[i];
+
+ /*
+ * Step 2:
+ * RES (test USIM), XRES (SS), CK, IK and AK are extracted from
+ * XDOUT.
+ */
+
+ /*
+ * RES[bits 0,1, ...n-1,n] = f2(XDOUT,n) = XDOUT[bits 0,1, . . .n-1,n]
+ */
+ memcpy(res, xdout, TS_34_108_RES_SIZE);
+
+ /*
+ * CK[bits 0,1, ...126,127] = f3(XDOUT) = XDOUT[bits 8,9, ...126,127,0,1, ...6,7]
+ */
+ memcpy(ck, xdout + 1, TS_34_108_CK_SIZE - 1);
+ ck[TS_34_108_CK_SIZE - 1] = xdout[0];
+
+ /*
+ * IK[bits0,1, ...126,127] = f4(XDOUT) = XDOUT[bits16,17, ...126,127,0,1, ...14,15]
+ */
+ p = ik;
+ memcpy(ik, xdout + 2, TS_34_108_CK_SIZE - 2);
+ ik[TS_34_108_CK_SIZE - 2] = xdout[0];
+ ik[TS_34_108_CK_SIZE - 1] = xdout[1];
+
+ /*
+ * AK[bits0,1, ...46,47] = f5(XDOUT) = XDOUT[bits24,25, ...70,71]
+ */
+ memcpy(ak, xdout + 3, TS_34_108_AK_SIZE);
+
+ /*
+ * Step 3:
+ * Concatenate SQN with AMF to obtain CDOUT
+ */
+ uint48_to_buff(sqn_buff, sqn);
+ p = cdout;
+ memcpy(p, sqn_buff, TS_34_108_SQN_SIZE);
+ p += TS_34_108_SQN_SIZE;
+ memcpy(p, amf, TS_34_108_AMF_SIZE);
+
+ /*
+ * Step 4:
+ * XMAC (test USIM) and MAC (SS) are calculated from XDOUT and CDOUT.
+ */
+ for (i = 0; i < sizeof(xmac); i++) xmac[i] = xdout[i] ^ cdout[i];
+
+ /*
+ * Step 5:
+ * The SS calculates the authentication token AUTN.
+ */
+ p = autn;
+ for (i = 0; i < sizeof(sqn_buff); i++) p[i] = sqn_buff[i] ^ ak[i];
+ p += sizeof(sqn_buff);
+ memcpy(p, amf, TS_34_108_AMF_SIZE);
+ p += TS_34_108_AMF_SIZE;
+ memcpy(p, xmac, TS_34_108_XMAC_SIZE);
+
+ return 0;
+}
--- /dev/null
+#pragma once
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file ts_34_108.h
+ * @brief Implementation of the TS.34.108 dummy USMI algorithm
+ *
+ * @copyright 2019 The FreeRADIUS server project
+ * @copyright 2019 Arran Cudbard-Bell \<a.cudbardb@freeradius.org\>
+ */
+
+/*
+ * Inputs
+ */
+#define TS_34_108_KI_SIZE 16 //!< Subscriber key.
+#define TS_34_108_OP_SIZE 16 //!< Operator code (unique to the operator)
+#define TS_34_108_AMF_SIZE 2 //!< Authentication management field.
+#define TS_34_108_SQN_SIZE 6 //!< Sequence number.
+#define TS_34_108_RAND_SIZE 16 //!< Random challenge.
+
+/*
+ * UMTS Outputs
+ */
+#define TS_34_108_AK_SIZE 6 //!< Anonymisation key.
+#define TS_34_108_AUTN_SIZE 16 //!< Network authentication key.
+#define TS_34_108_IK_SIZE 16 //!< Integrity key.
+#define TS_34_108_CK_SIZE 16 //!< Ciphering key.
+#define TS_34_108_RES_SIZE 16
+#define TS_34_108_AUTS_SIZE 14
+
+int ts_34_108_umts_generate(uint8_t autn[TS_34_108_AUTN_SIZE],
+ uint8_t ik[TS_34_108_IK_SIZE],
+ uint8_t ck[TS_34_108_CK_SIZE],
+ uint8_t ak[TS_34_108_AK_SIZE],
+ uint8_t res[TS_34_108_RES_SIZE],
+ uint8_t const amf[TS_34_108_AMF_SIZE],
+ uint8_t const ki[TS_34_108_KI_SIZE],
+ uint64_t sqn,
+ uint8_t const rand[TS_34_108_RAND_SIZE]);
SOURCES := $(TARGETNAME).c
-SRC_CFLAGS :=
-TGT_LDLIBS :=
-SRC_INCDIRS := ${top_srcdir}/src/modules/rlm_eap/ ${top_srcdir}/src/modules/rlm_eap/lib/base/ ${top_srcdir}/src/modules/rlm_eap/lib/sim/
-
-TGT_PREREQS := $(LIBFREERADIUS_SERVER) libfreeradius-radius.a libfreeradius-util.a libfreeradius-eap.a libfreeradius-eap-sim.a
+TGT_PREREQS := $(LIBFREERADIUS_SERVER) libfreeradius-radius.a libfreeradius-util.a libfreeradius-eap.a libfreeradius-sim.a libfreeradius-eap-aka-sim.a
/**
* $Id$
* @file rlm_eap_aka.c
- * @brief Implements the AKA part of EAP-AKA
+ * @brief Implements EAP-AKA
*
* @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
*
RCSID("$Id$")
#include <freeradius-devel/eap/base.h>
-#include <freeradius-devel/eap/types.h>
+#include <freeradius-devel/eap_aka_sim/state_machine.h>
#include <freeradius-devel/server/rad_assert.h>
-#include <freeradius-devel/sim/base.h>
#include <freeradius-devel/unlang/compile.h>
-
-#include <freeradius-devel/protocol/eap/aka/freeradius.h>
-#include <freeradius-devel/protocol/eap/aka/rfc4187.h>
-
-#include "eap_aka.h"
-
-#ifndef EAP_TLS_MPPE_KEY_LEN
-# define EAP_TLS_MPPE_KEY_LEN 32
-#endif
-
-static fr_table_num_ordered_t const aka_state_table[] = {
- { "IDENTITY", EAP_AKA_SERVER_IDENTITY },
- { "CHALLENGE", EAP_AKA_SERVER_CHALLENGE },
- { "SUCCESS-NOTIFICATION", EAP_AKA_SERVER_SUCCESS_NOTIFICATION },
- { "SUCCESS", EAP_AKA_SERVER_SUCCESS },
- { "FAILURE-NOTIFICATION", EAP_AKA_SERVER_FAILURE_NOTIFICATION },
- { "FAILURE", EAP_AKA_SERVER_FAILURE },
-};
-static size_t aka_state_table_len = NUM_ELEMENTS(aka_state_table);
-
-static int mod_section_compile(eap_aka_actions_t *actions, CONF_SECTION *server_cs);
-static int virtual_server_parse(TALLOC_CTX *ctx, void *out, void *parent,
- CONF_ITEM *ci, UNUSED CONF_PARSER const *rule);
+#include <freeradius-devel/unlang/module.h>
static CONF_PARSER submodule_config[] = {
- { FR_CONF_OFFSET("network_name", FR_TYPE_STRING | FR_TYPE_REQUIRED, rlm_eap_aka_t, network_name ) },
- { FR_CONF_OFFSET("request_identity", FR_TYPE_BOOL, rlm_eap_aka_t, request_identity ), .dflt = "no" },
- { FR_CONF_OFFSET("protected_success", FR_TYPE_BOOL, rlm_eap_aka_t, protected_success ), .dflt = "no" },
- { FR_CONF_OFFSET("virtual_server", FR_TYPE_VOID, rlm_eap_aka_t, actions), .func = virtual_server_parse },
+ { FR_CONF_OFFSET("request_identity", FR_TYPE_BOOL, eap_aka_sim_common_conf_t, request_identity ),
+ .func = cf_table_parse_uint32, .uctx = &(cf_table_parse_ctx_t){ .table = fr_aka_sim_id_request_table, .len = &fr_aka_sim_id_request_table_len }},
+ { FR_CONF_OFFSET("strip_permanent_identity_hint", FR_TYPE_BOOL, eap_aka_sim_common_conf_t,
+ strip_permanent_identity_hint ), .dflt = "yes" },
+ { FR_CONF_OFFSET("ephemeral_id_length", FR_TYPE_UINT8, eap_aka_sim_common_conf_t, ephemeral_id_length ), .dflt = "14" }, /* 14 for compatibility */
+ { FR_CONF_OFFSET("protected_success", FR_TYPE_BOOL, eap_aka_sim_common_conf_t, protected_success ), .dflt = "no" },
+ { FR_CONF_OFFSET_IS_SET("prefer_aka_prime", FR_TYPE_BOOL, eap_aka_sim_common_conf_t, send_at_bidding_prefer_prime ), .dflt = "no" },
+ { FR_CONF_OFFSET("virtual_server", FR_TYPE_VOID, eap_aka_sim_common_conf_t, virtual_server), .func = virtual_server_cf_parse },
CONF_PARSER_TERMINATOR
};
-static fr_dict_t *dict_freeradius;
-static fr_dict_t *dict_radius;
-static fr_dict_t *dict_eap_aka;
+static fr_dict_t *dict_eap_aka_sim;
extern fr_dict_autoload_t rlm_eap_aka_dict[];
fr_dict_autoload_t rlm_eap_aka_dict[] = {
- { .out = &dict_freeradius, .proto = "freeradius" },
- { .out = &dict_radius, .proto = "radius" },
- { .out = &dict_eap_aka, .proto = "eap-aka", .base_dir = "eap/aka" },
+ { .out = &dict_eap_aka_sim, .base_dir="eap/aka-sim", .proto = "eap-aka-sim" },
{ NULL }
};
-static fr_dict_attr_t const *attr_eap_aka_subtype;
-static fr_dict_attr_t const *attr_sim_amf;
-
-static fr_dict_attr_t const *attr_ms_mppe_send_key;
-static fr_dict_attr_t const *attr_ms_mppe_recv_key;
-
-static fr_dict_attr_t const *attr_eap_aka_any_id_req;
-static fr_dict_attr_t const *attr_eap_aka_autn;
-static fr_dict_attr_t const *attr_eap_aka_bidding;
-static fr_dict_attr_t const *attr_eap_aka_checkcode;
-static fr_dict_attr_t const *attr_eap_aka_client_error_code;
-static fr_dict_attr_t const *attr_eap_aka_encr_data;
-static fr_dict_attr_t const *attr_eap_aka_fullauth_id_req;
-static fr_dict_attr_t const *attr_eap_aka_identity;
-static fr_dict_attr_t const *attr_eap_aka_kdf;
-static fr_dict_attr_t const *attr_eap_aka_kdf_input;
-static fr_dict_attr_t const *attr_eap_aka_mac;
-static fr_dict_attr_t const *attr_eap_aka_notification;
-static fr_dict_attr_t const *attr_eap_aka_permanent_id_req;
-static fr_dict_attr_t const *attr_eap_aka_rand;
-static fr_dict_attr_t const *attr_eap_aka_res;
-static fr_dict_attr_t const *attr_eap_aka_result_ind;
-
-extern fr_dict_attr_autoload_t rlm_eap_aka_dict_attr[];
-fr_dict_attr_autoload_t rlm_eap_aka_dict_attr[] = {
-
- { .out = &attr_sim_amf, .name = "SIM-AMF", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
-
- { .out = &attr_ms_mppe_send_key, .name = "MS-MPPE-Send-Key", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
- { .out = &attr_ms_mppe_recv_key, .name = "MS-MPPE-Recv-Key", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
-
- { .out = &attr_eap_aka_any_id_req, .name = "EAP-AKA-Any-ID-Req", .type = FR_TYPE_BOOL, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_autn, .name = "EAP-AKA-AUTN", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_bidding, .name = "EAP-AKA-Bidding", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_checkcode, .name = "EAP-AKA-Checkcode", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_client_error_code, .name = "EAP-AKA-Client-Error-Code", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_encr_data, .name = "EAP-AKA-Encr-Data", .type = FR_TYPE_TLV, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_fullauth_id_req, .name = "EAP-AKA-Fullauth-ID-Req", .type = FR_TYPE_BOOL, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_identity, .name = "EAP-AKA-Identity", .type = FR_TYPE_STRING, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_kdf_input, .name = "EAP-AKA-KDF-Input", .type = FR_TYPE_STRING, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_kdf, .name = "EAP-AKA-KDF", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_mac, .name = "EAP-AKA-MAC", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_notification, .name = "EAP-AKA-Notification", .type = FR_TYPE_UINT16, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_permanent_id_req, .name = "EAP-AKA-Permanent-ID-Req", .type = FR_TYPE_BOOL, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_rand, .name = "EAP-AKA-RAND", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_res, .name = "EAP-AKA-RES", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_result_ind, .name = "EAP-AKA-Result-Ind", .type = FR_TYPE_BOOL, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_subtype, .name = "EAP-AKA-Subtype", .type = FR_TYPE_UINT32, .dict = &dict_eap_aka },
- { NULL }
-};
-
-static int virtual_server_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
- CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
-{
- CONF_SECTION *server_cs;
-
- if (virtual_server_has_namespace(&server_cs, cf_pair_value(cf_item_to_pair(ci)),
- dict_eap_aka, ci) < 0) return -1;
-
- if (mod_section_compile(out, server_cs) < 0) return -1;
-
- return 0;
-}
-
-static int eap_aka_compose(eap_session_t *eap_session)
-{
- eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
- fr_cursor_t cursor;
- fr_cursor_t to_encode;
- VALUE_PAIR *head = NULL, *vp;
- REQUEST *request = eap_session->request;
- ssize_t ret;
- fr_sim_encode_ctx_t encoder_ctx = {
- .root = fr_dict_root(dict_eap_aka),
- .keys = &eap_aka_session->keys,
-
- .iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
- .iv_included = false,
-
- .hmac_md = eap_aka_session->mac_md,
- .eap_packet = eap_session->this_round->request,
- .hmac_extra = NULL,
- .hmac_extra_len = 0
- };
-
- fr_cursor_init(&cursor, &eap_session->request->reply->vps);
- fr_cursor_init(&to_encode, &head);
-
- while ((vp = fr_cursor_current(&cursor))) {
- if (!fr_dict_parent_common(encoder_ctx.root, vp->da, true)) {
- fr_cursor_next(&cursor);
- continue;
- }
- vp = fr_cursor_remove(&cursor);
-
- /*
- * Silently discard encrypted attributes until
- * the peer should have k_encr. These can be
- * added by policy, and seem to cause
- * wpa_supplicant to fail if sent before the challenge.
- */
- if (!eap_aka_session->allow_encrypted && fr_dict_parent_common(attr_eap_aka_encr_data, vp->da, true)) {
- RWDEBUG("Silently discarding &reply:%s: Encrypted attributes not allowed in this round",
- vp->da->name);
- talloc_free(vp);
- continue;
- }
-
- fr_cursor_append(&to_encode, vp);
- }
-
- RDEBUG2("Encoding EAP-AKA attributes");
- log_request_pair_list(L_DBG_LVL_2, request, head, NULL);
-
- eap_session->this_round->request->type.num = eap_aka_session->type;
- eap_session->this_round->request->id = eap_aka_session->aka_id++ & 0xff;
- eap_session->this_round->set_request_id = true;
-
- ret = fr_sim_encode(eap_session->request, head, &encoder_ctx);
- fr_cursor_head(&to_encode);
- fr_cursor_free_list(&to_encode);
-
- if (ret < 0) {
- RPEDEBUG("Failed encoding EAP-AKA data");
- return -1;
- }
- return 0;
-}
-
-/** Send an EAP-AKA identity request to the supplicant
- *
- * There are three types of user identities that can be implemented
- * - Permanent identities such as 0123456789098765@myoperator.com
- * Permanent identities can be identified by the leading zero followed by
- * by 15 digits (the IMSI number).
- * - Ephemeral identities (pseudonyms). These are identities assigned for
- * identity privacy so the user can't be tracked. These can identities
- * can either be generated as per the 3GPP 'Security aspects of non-3GPP accesses'
- * document section 14, where a set of up to 16 encryption keys are used
- * to reversibly encrypt the IMSI. Alternatively the pseudonym can be completely
- * randomised and stored in a datastore.
- * - A fast resumption ID which resolves to data used for fast resumption.
- *
- * In order to perform full authentication the original IMSI is required for
- * forwarding to the HLR. In the case where we can't match/decrypt the pseudonym,
- * or can't perform fast resumption, we need to request the full identity from
- * the supplicant.
- *
- * @param[in] eap_session to continue.
- * @return
- * - 0 on success.
- * - <0 on failure.
- */
-static int eap_aka_send_identity_request(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
- VALUE_PAIR *vp;
- RADIUS_PACKET *packet;
- fr_cursor_t cursor;
-
- RDEBUG2("Sending AKA-Identity (%s)", fr_table_str_by_value(sim_id_request_table, eap_aka_session->id_req, "<INVALID>"));
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
- eap_aka_session->allow_encrypted = false; /* In case this is after failed fast-resumption */
-
- packet = request->reply;
- fr_cursor_init(&cursor, &packet->vps);
-
- /*
- * Set the subtype to identity request
- */
- vp = fr_pair_afrom_da(packet, attr_eap_aka_subtype);
- vp->vp_uint16 = FR_EAP_AKA_SUBTYPE_VALUE_AKA_IDENTITY;
- fr_cursor_append(&cursor, vp);
-
- /*
- * Select the right type of identity request attribute
- */
- switch (eap_aka_session->id_req) {
- case SIM_ANY_ID_REQ:
- vp = fr_pair_afrom_da(packet, attr_eap_aka_any_id_req);
- break;
-
- case SIM_PERMANENT_ID_REQ:
- vp = fr_pair_afrom_da(packet, attr_eap_aka_permanent_id_req);
- break;
-
- case SIM_FULLAUTH_ID_REQ:
- vp = fr_pair_afrom_da(packet, attr_eap_aka_fullauth_id_req);
- break;
-
- default:
- rad_assert(0);
- }
- vp->vp_bool = true;
- fr_cursor_append(&cursor, vp);
-
- /*
- * Encode the packet
- */
- if (eap_aka_compose(eap_session) < 0) {
- failure:
- fr_pair_list_free(&packet->vps);
- return -1;
- }
-
- /*
- * Digest the packet contents, updating our checkcode.
- */
- if (!eap_aka_session->checkcode_state &&
- fr_sim_crypto_init_checkcode(eap_aka_session, &eap_aka_session->checkcode_state,
- eap_aka_session->checkcode_md) < 0) {
- RPEDEBUG("Failed initialising checkcode");
- goto failure;
- }
- if (fr_sim_crypto_update_checkcode(eap_aka_session->checkcode_state, eap_session->this_round->request) < 0) {
- RPEDEBUG("Failed updating checkcode");
- goto failure;
- }
-
- return 0;
-}
-
-/** Send the challenge itself
- *
- * Challenges will come from one of three places eventually:
- *
- * 1 from attributes like FR_EAP_AKA_RANDx
- * (these might be retrieved from a database)
- *
- * 2 from internally implemented SIM authenticators
- * (a simple one based upon XOR will be provided)
- *
- * 3 from some kind of SS7 interface.
- *
- * For now, they only come from attributes.
- * It might be that the best way to do 2/3 will be with a different
- * module to generate/calculate things.
- */
-static int eap_aka_send_challenge(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
- VALUE_PAIR **to_peer, *vp;
- RADIUS_PACKET *packet;
- fr_sim_vector_src_t src = SIM_VECTOR_SRC_AUTO;
-
- rad_assert(request);
- rad_assert(request->reply);
-
- /*
- * to_peer is the data to the client
- */
- packet = eap_session->request->reply;
- to_peer = &packet->vps;
-
- RDEBUG2("Acquiring UMTS vector(s)");
-
- /*
- * Toggle the AMF high bit to indicate we're doing AKA'
- */
- if (eap_aka_session->type == FR_EAP_METHOD_AKA_PRIME) {
- uint8_t amf_buff[2] = { 0x80, 0x00 }; /* Set the AMF separation bit high */
-
- MEM(pair_update_control(&vp, attr_sim_amf) >= 0);
- fr_pair_value_memcpy(vp, amf_buff, sizeof(amf_buff), false);
- }
-
- /*
- * Get vectors from attribute or generate
- * them using COMP128-* or Milenage.
- */
- if (fr_sim_vector_umts_from_attrs(request, request->control, &eap_aka_session->keys, &src) != 0) {
- REDEBUG("Failed retrieving UMTS vectors");
- return RLM_MODULE_FAIL;
- }
-
- /*
- * Don't leave the AMF hanging around
- */
- if (eap_aka_session->type == FR_EAP_METHOD_AKA_PRIME) pair_delete_control(attr_sim_amf);
-
- /*
- * All set, calculate keys!
- */
- switch (eap_aka_session->kdf) {
- case FR_EAP_AKA_KDF_VALUE_EAP_AKA_PRIME_WITH_CK_PRIME_IK_PRIME:
- fr_sim_crypto_kdf_1_umts(&eap_aka_session->keys);
- break;
-
- default:
- fr_sim_crypto_kdf_0_umts(&eap_aka_session->keys);
- break;
- }
- if (RDEBUG_ENABLED3) fr_sim_crypto_keys_log(request, &eap_aka_session->keys);
-
- RDEBUG2("Sending AKA-Challenge");
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
-
- /*
- * Set the subtype to challenge
- */
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_subtype));
- vp->vp_uint16 = FR_EAP_AKA_SUBTYPE_VALUE_AKA_CHALLENGE;
- fr_pair_replace(to_peer, vp);
-
- /*
- * Indicate we'd like to use protected success messages
- */
- if (eap_aka_session->send_result_ind) {
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_result_ind));
- vp->vp_bool = true;
- fr_pair_replace(to_peer, vp);
- }
-
- /*
- * We support EAP-AKA' and the peer should use that
- * if it's able to...
- */
- if (eap_aka_session->send_at_bidding) {
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_bidding));
- vp->vp_uint16 = FR_EAP_AKA_BIDDING_VALUE_PREFER_AKA_PRIME;
- fr_pair_replace(to_peer, vp);
- }
-
- /*
- * Send the network name and KDF to the peer
- */
- if (eap_aka_session->type == FR_EAP_METHOD_AKA_PRIME) {
- if (!eap_aka_session->keys.network_len) {
- REDEBUG2("No network name available, can't set EAP-AKA-KDF-Input");
- failure:
- fr_pair_list_free(&packet->vps);
- return -1;
- }
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_kdf_input));
- fr_pair_value_bstrncpy(vp, eap_aka_session->keys.network, eap_aka_session->keys.network_len);
- fr_pair_replace(to_peer, vp);
-
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_kdf));
- vp->vp_uint16 = eap_aka_session->kdf;
- fr_pair_replace(to_peer, vp);
- }
-
- /*
- * Okay, we got the challenge! Put it into an attribute.
- */
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_rand));
- fr_pair_value_memcpy(vp, eap_aka_session->keys.umts.vector.rand, SIM_VECTOR_UMTS_RAND_SIZE, false);
- fr_pair_replace(to_peer, vp);
-
- /*
- * Send the AUTN value to the client, so it can authenticate
- * whoever has knowledge of the Ki.
- */
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_autn));
- fr_pair_value_memcpy(vp, eap_aka_session->keys.umts.vector.autn, SIM_VECTOR_UMTS_AUTN_SIZE, false);
- fr_pair_replace(to_peer, vp);
-
- /*
- * need to include an AT_MAC attribute so that it will get
- * calculated.
- */
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_mac));
- fr_pair_replace(to_peer, vp);
-
- /*
- * If we have checkcode data, send that to the peer
- * for validation.
- */
- if (eap_aka_session->checkcode_state) {
- ssize_t slen;
-
- slen = fr_sim_crypto_finalise_checkcode(eap_aka_session->checkcode, &eap_aka_session->checkcode_state);
- if (slen < 0) {
- RPEDEBUG("Failed calculating checkcode");
- goto failure;
- }
- eap_aka_session->checkcode_len = slen;
-
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_checkcode));
- fr_pair_value_memcpy(vp, eap_aka_session->checkcode, slen, false);
- /*
- * If we don't have checkcode data, then we exchanged
- * no identity packets, so checkcode is zero.
- */
- } else {
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_aka_checkcode));
- eap_aka_session->checkcode_len = 0;
- }
- fr_pair_replace(to_peer, vp);
-
- /*
- * We've sent the challenge so the peer should now be able
- * to accept encrypted attributes.
- */
- eap_aka_session->allow_encrypted = true;
-
- /*
- * Encode the packet
- */
- if (eap_aka_compose(eap_session) < 0) goto failure;
-
- return 0;
-}
-
-/** Send a success notification
- *
- */
-static int eap_aka_send_eap_success_notification(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- RADIUS_PACKET *packet = eap_session->request->reply;
- eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
- fr_cursor_t cursor;
- VALUE_PAIR *vp;
-
- RDEBUG2("Sending AKA-Notification (Success)");
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
-
- if (!fr_cond_assert(eap_aka_session->challenge_success)) return -1;
-
- fr_cursor_init(&cursor, &packet->vps);
-
- /*
- * Set the subtype to notification
- */
- vp = fr_pair_afrom_da(packet, attr_eap_aka_subtype);
- vp->vp_uint16 = FR_EAP_AKA_SUBTYPE_VALUE_AKA_NOTIFICATION;
- fr_cursor_append(&cursor, vp);
-
- vp = fr_pair_afrom_da(packet, attr_eap_aka_notification);
- vp->vp_uint16 = FR_EAP_AKA_NOTIFICATION_VALUE_SUCCESS;
- fr_cursor_append(&cursor, vp);
-
- /*
- * Need to include an AT_MAC attribute so that it will get
- * calculated.
- */
- vp = fr_pair_afrom_da(packet, attr_eap_aka_mac);
- fr_pair_replace(&packet->vps, vp);
-
- /*
- * Encode the packet
- */
- if (eap_aka_compose(eap_session) < 0) {
- fr_pair_list_free(&packet->vps);
- return -1;
- }
-
- return 0;
-}
-
-/** Send a success message with MPPE-keys
- *
- * The only work to be done is the add the appropriate SEND/RECV
- * attributes derived from the MSK.
- */
-static int eap_aka_send_eap_success(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- uint8_t *p;
- eap_aka_session_t *eap_aka_session;
-
- RDEBUG2("Sending EAP-Success");
-
- eap_session->this_round->request->code = FR_EAP_CODE_SUCCESS;
- eap_session->finished = true;
-
- eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
-
- p = eap_aka_session->keys.msk;
- eap_add_reply(eap_session->request, attr_ms_mppe_recv_key, p, EAP_TLS_MPPE_KEY_LEN);
- p += EAP_TLS_MPPE_KEY_LEN;
- eap_add_reply(eap_session->request, attr_ms_mppe_send_key, p, EAP_TLS_MPPE_KEY_LEN);
-
- return 0;
-}
-
-/** Send a failure message
- *
- */
-static int eap_aka_send_eap_failure_notification(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- RADIUS_PACKET *packet = eap_session->request->reply;
- fr_cursor_t cursor;
- VALUE_PAIR *vp;
- eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
-
- fr_cursor_init(&cursor, &packet->vps);
-
- vp = fr_pair_find_by_da(packet->vps, attr_eap_aka_notification, TAG_ANY);
- if (!vp) {
- vp = fr_pair_afrom_da(packet, attr_eap_aka_notification);
- vp->vp_uint16 = FR_EAP_AKA_NOTIFICATION_VALUE_GENERAL_FAILURE;
- fr_cursor_append(&cursor, vp);
- }
-
- /*
- * Change the failure notification depending where
- * we are in the state machine.
- */
- if (eap_aka_session->challenge_success) {
- vp->vp_uint16 &= ~0x4000; /* Unset phase bit */
- } else {
- vp->vp_uint16 |= 0x4000; /* Set phase bit */
- }
- vp->vp_uint16 &= ~0x8000; /* In both cases success bit should be low */
-
- RDEBUG2("Sending AKA-Notification (%pV)", &vp->data);
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
-
- /*
- * Set the subtype to notification
- */
- vp = fr_pair_afrom_da(packet, attr_eap_aka_subtype);
- vp->vp_uint16 = FR_EAP_AKA_SUBTYPE_VALUE_AKA_NOTIFICATION;
- fr_cursor_append(&cursor, vp);
-
- /*
- * If we're after the challenge phase
- * then we need to include a MAC to
- * protect notifications.
- */
- if (eap_aka_session->challenge_success) {
- vp = fr_pair_afrom_da(packet, attr_eap_aka_mac);
- fr_pair_replace(&packet->vps, vp);
- }
-
- /*
- * Encode the packet
- */
- if (eap_aka_compose(eap_session) < 0) {
- fr_pair_list_free(&packet->vps);
- return -1;
- }
-
- return 0;
-}
-
-static int eap_aka_send_eap_failure(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
-
- RDEBUG2("Sending EAP-Failure");
-
- eap_session->this_round->request->code = FR_EAP_CODE_FAILURE;
- eap_session->finished = true;
-
- return 0;
-}
-
-/** Run the server state machine
- *
- */
-static void eap_aka_state_enter(eap_session_t *eap_session, eap_aka_server_state_t new_state)
-{
- REQUEST *request = eap_session->request;
- eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
-
- if (new_state != eap_aka_session->state) {
- RDEBUG2("Changed state %s -> %s",
- fr_table_str_by_value(aka_state_table, eap_aka_session->state, "<unknown>"),
- fr_table_str_by_value(aka_state_table, new_state, "<unknown>"));
- eap_aka_session->state = new_state;
- } else {
- RDEBUG2("Reentering state %s",
- fr_table_str_by_value(aka_state_table, eap_aka_session->state, "<unknown>"));
- }
-
- switch (new_state) {
- /*
- * Send an EAP-AKA Identity request
- */
- case EAP_AKA_SERVER_IDENTITY:
- if (eap_aka_send_identity_request(eap_session) < 0) {
- notify_failure:
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return;
- }
- break;
-
- /*
- * Send the EAP-AKA Challenge message.
- */
- case EAP_AKA_SERVER_CHALLENGE:
- if (eap_aka_send_challenge(eap_session) < 0) goto notify_failure;
- break;
-
- /*
- * Sent a protected success notification
- */
- case EAP_AKA_SERVER_SUCCESS_NOTIFICATION:
- if (eap_aka_send_eap_success_notification(eap_session) < 0) goto notify_failure;
- break;
-
- /*
- * Send the EAP Success message (we're done)
- */
- case EAP_AKA_SERVER_SUCCESS:
- if (eap_aka_send_eap_success(eap_session) < 0) goto notify_failure;
- return;
-
- /*
- * Send a general failure notification
- */
- case EAP_AKA_SERVER_FAILURE_NOTIFICATION:
- if (eap_aka_send_eap_failure_notification(eap_session) < 0) { /* Fallback to EAP-Failure */
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
- }
- return;
-
- /*
- * Send an EAP-Failure (we're done)
- */
- case EAP_AKA_SERVER_FAILURE:
- eap_aka_send_eap_failure(eap_session);
- return;
-
- default:
- rad_assert(0); /* Invalid transition */
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return;
- }
-}
-
-static int process_eap_aka_identity(eap_session_t *eap_session, VALUE_PAIR *vps)
-{
- REQUEST *request = eap_session->request;
- eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
- VALUE_PAIR *id;
- fr_sim_id_type_t type = SIM_ID_TYPE_UNKNOWN;
- fr_sim_method_hint_t method = SIM_METHOD_HINT_UNKNOWN;
-
- /*
- * Digest the identity response
- */
- if (fr_sim_crypto_update_checkcode(eap_aka_session->checkcode_state, eap_session->this_round->response) < 0) {
- RPEDEBUG("Failed updating checkcode");
- return -1;
- }
-
- /*
- * See if we got an AT_IDENTITY
- */
- id = fr_pair_find_by_da(vps, attr_eap_aka_identity, TAG_ANY);
- if (id) {
- if (fr_sim_id_type(&type, &method,
- eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) {
- RPWDEBUG2("Failed parsing identity");
- }
- /*
- * Update cryptographic identity
- */
- talloc_const_free(eap_aka_session->keys.identity);
- eap_aka_session->keys.identity_len = id->vp_length;
- MEM(eap_aka_session->keys.identity = talloc_memdup(eap_aka_session, id->vp_strvalue, id->vp_length));
- }
-
- /*
- * @TODO Run a virtual server to see if we can use the
- * identity we just acquired, or whether we need to
- * negotiate the next permissive ID.
- */
-
- /*
- * Negotiate the next permissive form
- * if identity, or fail.
- */
- switch (eap_aka_session->id_req) {
- case SIM_ANY_ID_REQ:
- eap_aka_session->id_req = SIM_FULLAUTH_ID_REQ;
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_IDENTITY);
- break;
-
- case SIM_FULLAUTH_ID_REQ:
- eap_aka_session->id_req = SIM_PERMANENT_ID_REQ;
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_IDENTITY);
- break;
-
- case SIM_PERMANENT_ID_REQ:
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_CHALLENGE);
-// REDEBUG2("Failed to negotiate a usable identity");
-// eap_aka_state_enter(eap_session, eap_aka_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- break;
-
- case SIM_NO_ID_REQ:
- rad_assert(0);
- return -1;
- }
-
- return 0;
-}
-
-/** Process an EAP-AKA/Response/Challenge
- *
- * Verify that MAC, and RES match what we expect.
- */
-static int process_eap_aka_challenge(eap_session_t *eap_session, VALUE_PAIR *vps)
-{
- REQUEST *request = eap_session->request;
- eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
-
- uint8_t calc_mac[SIM_MAC_DIGEST_SIZE];
- ssize_t slen;
- VALUE_PAIR *vp = NULL, *mac, *checkcode;
-
- mac = fr_pair_find_by_da(vps, attr_eap_aka_mac, TAG_ANY);
- if (!mac) {
- REDEBUG("Missing AT_MAC attribute");
- return -1;
- }
- if (mac->vp_length != SIM_MAC_DIGEST_SIZE) {
- REDEBUG("EAP-AKA-MAC has incorrect length, expected %u bytes got %zu bytes",
- SIM_MAC_DIGEST_SIZE, mac->vp_length);
- return -1;
- }
-
- slen = fr_sim_crypto_sign_packet(calc_mac, eap_session->this_round->response, true,
- eap_aka_session->mac_md,
- eap_aka_session->keys.k_aut, eap_aka_session->keys.k_aut_len,
- NULL, 0);
- if (slen < 0) {
- RPEDEBUG("Failed calculating MAC");
- return -1;
- } else if (slen == 0) {
- REDEBUG("Missing EAP-AKA-MAC attribute in packet buffer");
- return -1;
- }
-
- if (memcmp(mac->vp_octets, calc_mac, sizeof(calc_mac)) == 0) {
- RDEBUG2("EAP-AKA-MAC matches calculated MAC");
- } else {
- REDEBUG("EAP-AKA-MAC does not match calculated MAC");
- RHEXDUMP_INLINE2(mac->vp_octets, SIM_MAC_DIGEST_SIZE, "Received");
- RHEXDUMP_INLINE2(calc_mac, SIM_MAC_DIGEST_SIZE, "Expected");
- return -1;
- }
-
- /*
- * If the peer doesn't include a checkcode then that
- * means they don't support it, and we can't validate
- * their view of the identity packets.
- */
- checkcode = fr_pair_find_by_da(vps, attr_eap_aka_checkcode, TAG_ANY);
- if (checkcode) {
- if (checkcode->vp_length != eap_aka_session->checkcode_len) {
- REDEBUG("Checkcode length (%zu) does not match calculated checkcode length (%zu)",
- checkcode->vp_length, eap_aka_session->checkcode_len);
- return -1;
- }
-
- if (memcmp(checkcode->vp_octets, eap_aka_session->checkcode, eap_aka_session->checkcode_len) == 0) {
- RDEBUG2("EAP-AKA-Checkcode matches calculated checkcode");
- } else {
- REDEBUG("EAP-AKA-Checkcode does not match calculated checkcode");
- RHEXDUMP_INLINE2(checkcode->vp_octets, checkcode->vp_length, "Received");
- RHEXDUMP_INLINE2(eap_aka_session->checkcode,
- eap_aka_session->checkcode_len, "Expected");
- return -1;
- }
- /*
- * Only print something if we calculated a checkcode
- */
- } else if (eap_aka_session->checkcode_len > 0){
- RDEBUG2("Peer didn't include EAP-AKA-Checkcode, skipping checkcode validation");
- }
-
- vp = fr_pair_find_by_da(vps, attr_eap_aka_res, TAG_ANY);
- if (!vp) {
- REDEBUG("Missing EAP-AKA-RES from challenge response");
- return -1;
- }
-
- if (vp->vp_length != eap_aka_session->keys.umts.vector.xres_len) {
- REDEBUG("EAP-AKA-RES length (%zu) does not match XRES length (%zu)",
- vp->vp_length, eap_aka_session->keys.umts.vector.xres_len);
- return -1;
- }
-
- if (memcmp(vp->vp_octets, eap_aka_session->keys.umts.vector.xres, vp->vp_length)) {
- REDEBUG("EAP-AKA-RES from client does match XRES");
- RHEXDUMP_INLINE2(vp->vp_octets, vp->vp_length, "RES :");
- RHEXDUMP_INLINE2(eap_aka_session->keys.umts.vector.xres,
- eap_aka_session->keys.umts.vector.xres_len, "XRES :");
- return -1;
- }
-
- RDEBUG2("EAP-AKA-RES matches XRES");
-
- eap_aka_session->challenge_success = true;
-
- /*
- * If the peer wants a Success notification, then
- * send a success notification, otherwise send a
- * normal EAP-Success.
- */
- if (fr_pair_find_by_da(vps, attr_eap_aka_result_ind, TAG_ANY)) {
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_SUCCESS_NOTIFICATION);
- return 1;
- }
-
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_SUCCESS);
- return 0;
-}
-
-/** Process the Peer's response and advantage the state machine
- *
- */
-static rlm_rcode_t mod_process(UNUSED void *instance, UNUSED void *thread, REQUEST *request)
-{
- eap_session_t *eap_session = eap_session_get(request);
- eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
-
- fr_sim_decode_ctx_t ctx = {
- .keys = &eap_aka_session->keys,
- };
- VALUE_PAIR *vp, *vps, *subtype_vp;
- fr_cursor_t cursor;
-
- eap_aka_subtype_t subtype;
-
- int ret;
-
- /*
- * RFC 4187 says we ignore the contents of the
- * next packet after we send our success notification
- * and always send a success.
- */
- if (eap_aka_session->state == EAP_AKA_SERVER_SUCCESS_NOTIFICATION) {
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_SUCCESS);
- return RLM_MODULE_HANDLED;
- }
-
- /* vps is the data from the client */
- vps = request->packet->vps;
-
- fr_cursor_init(&cursor, &request->packet->vps);
- fr_cursor_tail(&cursor);
-
- ret = fr_sim_decode(eap_session->request,
- &cursor,
- dict_eap_aka,
- eap_session->this_round->response->type.data,
- eap_session->this_round->response->type.length,
- &ctx);
- /*
- * RFC 4187 says we *MUST* notify, not just
- * send an EAP-Failure in this case where
- * we cannot decode an EAP-AKA packet.
- */
- if (ret < 0) {
- RPEDEBUG2("Failed decoding EAP-AKA attributes");
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
-
- vp = fr_cursor_current(&cursor);
- if (vp && RDEBUG_ENABLED2) {
- RDEBUG2("EAP-AKA decoded attributes");
- log_request_pair_list(L_DBG_LVL_2, request, vp, NULL);
- }
-
- subtype_vp = fr_pair_find_by_da(vps, attr_eap_aka_subtype, TAG_ANY);
- if (!subtype_vp) {
- REDEBUG("Missing EAP-AKA-Subtype");
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
- subtype = subtype_vp->vp_uint16;
-
- switch (eap_aka_session->state) {
- /*
- * Here we expected the peer to send
- * us identities for validation.
- */
- case EAP_AKA_SERVER_IDENTITY:
- switch (subtype) {
- case EAP_AKA_IDENTITY:
- if (process_eap_aka_identity(eap_session, vps) == 0) return RLM_MODULE_HANDLED;
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
-
- /*
- * Case 1 where we're allowed to send an EAP-Failure
- *
- * This can happen in the case of a conservative
- * peer, where it refuses to provide the permanent
- * identity.
- */
- case EAP_AKA_CLIENT_ERROR:
- {
- char buff[20];
-
- vp = fr_pair_find_by_da(vps, attr_eap_aka_client_error_code, TAG_ANY);
- if (!vp) {
- REDEBUG("EAP-AKA Peer rejected AKA-Identity (%s) with client-error message but "
- "has not supplied a client error code",
- fr_table_str_by_value(sim_id_request_table, eap_aka_session->id_req, "<INVALID>"));
- } else {
- REDEBUG("Client rejected AKA-Identity (%s) with error: %s (%i)",
- fr_table_str_by_value(sim_id_request_table, eap_aka_session->id_req, "<INVALID>"),
- fr_pair_value_enum(vp, buff), vp->vp_uint16);
- }
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
- return RLM_MODULE_REJECT;
- }
-
- case EAP_AKA_NOTIFICATION:
- notification:
- {
- char buff[20];
-
- vp = fr_pair_afrom_da(vps, attr_eap_aka_notification);
- if (!vp) {
- REDEBUG2("Received AKA-Notification with no notification code");
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
-
- /*
- * Case 3 where we're allowed to send an EAP-Failure
- */
- if (!(vp->vp_uint16 & 0x8000)) {
- REDEBUG2("AKA-Notification %s (%i) indicates failure", fr_pair_value_enum(vp, buff),
- vp->vp_uint16);
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
- return RLM_MODULE_REJECT;
- }
-
- /*
- * ...if it's not a failure, then re-enter the
- * current state.
- */
- REDEBUG2("Got AKA-Notification %s (%i)", fr_pair_value_enum(vp, buff), vp->vp_uint16);
- eap_aka_state_enter(eap_session, eap_aka_session->state);
- return RLM_MODULE_HANDLED;
- }
-
- default:
- unexpected_subtype:
- /*
- * RFC 4187 says we *MUST* notify, not just
- * send an EAP-Failure in this case.
- */
- REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
-
- /*
- * Process the response to our previous challenge.
- */
- case EAP_AKA_SERVER_CHALLENGE:
- switch (subtype) {
- case EAP_AKA_CHALLENGE:
- switch (process_eap_aka_challenge(eap_session, vps)) {
- case 1:
- return RLM_MODULE_HANDLED;
-
- case 0:
- return RLM_MODULE_OK;
-
- case -1:
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
-
-
- case EAP_AKA_SYNCHRONIZATION_FAILURE:
- REDEBUG("EAP-AKA Peer synchronization failure"); /* We can't handle these yet */
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
-
- /*
- * Case 1 where we're allowed to send an EAP-Failure
- */
- case EAP_AKA_CLIENT_ERROR:
- {
- char buff[20];
-
- vp = fr_pair_find_by_da(vps, attr_eap_aka_client_error_code, TAG_ANY);
- if (!vp) {
- REDEBUG("EAP-AKA Peer rejected AKA-Challenge with client-error message but "
- "has not supplied a client error code");
- } else {
- REDEBUG("Client rejected AKA-Challenge with error: %s (%i)",
- fr_pair_value_enum(vp, buff), vp->vp_uint16);
- }
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
- return RLM_MODULE_REJECT;
- }
-
- /*
- * Case 2 where we're allowed to send an EAP-Failure
- */
- case EAP_AKA_AUTHENTICATION_REJECT:
- REDEBUG("EAP-AKA Peer Rejected AUTN");
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
- return RLM_MODULE_REJECT;
-
- case EAP_AKA_NOTIFICATION:
- goto notification;
-
- default:
- goto unexpected_subtype;
- }
-
- /*
- * Peer acked our failure
- */
- case EAP_AKA_SERVER_FAILURE_NOTIFICATION:
- switch (subtype) {
- case EAP_AKA_NOTIFICATION:
- RDEBUG2("AKA-Notification ACKed, sending EAP-Failure");
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
- return RLM_MODULE_REJECT;
-
- default:
- goto unexpected_subtype;
- }
-
- /*
- * Something bad happened...
- */
- default:
- rad_assert(0);
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
-}
-
-/** Initiate the EAP-SIM session by starting the state machine
- *
- */
-static rlm_rcode_t mod_session_init(void *instance, UNUSED void *thread, REQUEST *request)
-{
- rlm_eap_aka_t *inst = talloc_get_type_abort(instance, rlm_eap_aka_t);
- eap_session_t *eap_session = eap_session_get(request);
- eap_aka_session_t *eap_aka_session;
-
- fr_sim_id_type_t type;
- fr_sim_method_hint_t method;
-
- MEM(eap_aka_session = talloc_zero(eap_session, eap_aka_session_t));
-
- eap_session->opaque = eap_aka_session;
-
- /*
- * Set default configuration, we may allow these
- * to be toggled by attributes later.
- */
- eap_aka_session->request_identity = inst->request_identity;
- eap_aka_session->send_result_ind = inst->protected_success;
- eap_aka_session->id_req = SIM_NO_ID_REQ; /* Set the default */
-
- /*
- * This value doesn't have be strong, but it is
- * good if it is different now and then.
- */
- eap_aka_session->aka_id = (fr_rand() & 0xff);
-
- /*
- * Process the identity that we received in the
- * EAP-Identity-Response and use it to determine
- * the initial request we send to the Supplicant.
- */
- if (fr_sim_id_type(&type, &method,
- eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) {
- RPWDEBUG2("Failed parsing identity, continuing anyway");
- }
-
- /*
- * Unless AKA-Prime is explicitly disabled,
- * use it... It has stronger keying, and
- * binds authentication to the network.
- */
- switch (eap_session->type) {
- case FR_EAP_METHOD_AKA_PRIME:
- default:
- RDEBUG2("New EAP-AKA' session");
- eap_aka_session->type = FR_EAP_METHOD_AKA_PRIME;
- eap_aka_session->kdf = FR_EAP_AKA_KDF_VALUE_EAP_AKA_PRIME_WITH_CK_PRIME_IK_PRIME;
- eap_aka_session->checkcode_md = eap_aka_session->mac_md = EVP_sha256();
- eap_aka_session->keys.network = (uint8_t *)talloc_bstrndup(eap_aka_session, inst->network_name,
- talloc_array_length(inst->network_name) - 1);
- eap_aka_session->keys.network_len = talloc_array_length(eap_aka_session->keys.network) - 1;
- switch (method) {
- default:
- RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're "
- "attempting EAP-AKA'", fr_table_str_by_value(sim_id_method_hint_table, method, "<INVALID>"));
- break;
-
- case SIM_METHOD_HINT_AKA_PRIME:
- case SIM_METHOD_HINT_UNKNOWN:
- break;
- }
- break;
-
- case FR_EAP_METHOD_AKA:
- RDEBUG2("New EAP-AKA session");
- eap_aka_session->type = FR_EAP_METHOD_AKA;
- eap_aka_session->kdf = FR_EAP_AKA_KDF_VALUE_EAP_AKA; /* Not actually sent */
- eap_aka_session->checkcode_md = eap_aka_session->mac_md = EVP_sha1();
- eap_aka_session->send_at_bidding = true;
- switch (method) {
- default:
- RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're "
- "attempting EAP-AKA", fr_table_str_by_value(sim_id_method_hint_table, method, "<INVALID>"));
- break;
-
- case SIM_METHOD_HINT_AKA:
- case SIM_METHOD_HINT_UNKNOWN:
- break;
- }
- break;
- }
- eap_session->process = mod_process;
-
- /*
- * Admin wants us to always request an identity
- * initially. The RFC says this is also the
- * better way to operate, as the supplicant
- * can 'decorate' the identity in the identity
- * response.
- */
- if (inst->request_identity) {
- request_id:
- /*
- * We always start by requesting
- * any ID initially as we can
- * always negotiate down.
- */
- eap_aka_session->id_req = SIM_ANY_ID_REQ;
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_IDENTITY);
- return RLM_MODULE_HANDLED;
- }
- /*
- * Figure out what type of identity we have
- * and use it to determine the initial
- * request we send.
- */
- switch (type) {
- /*
- * If there's no valid tag on the identity
- * then it's probably been decorated by the
- * supplicant.
- *
- * Request the unmolested identity
- */
- case SIM_ID_TYPE_UNKNOWN:
- RWDEBUG("Identity format unknown, sending Identity request");
- goto request_id;
-
- /*
- * These types need to be transformed into something
- * usable before we can do anything.
- */
- case SIM_ID_TYPE_PSEUDONYM:
- case SIM_ID_TYPE_FASTAUTH:
-
- /*
- * Permanent ID means we can just send the challenge
- */
- case SIM_ID_TYPE_PERMANENT:
- eap_aka_session->keys.identity_len = talloc_array_length(eap_session->identity) - 1;
- MEM(eap_aka_session->keys.identity = talloc_memdup(eap_aka_session, eap_session->identity,
- eap_aka_session->keys.identity_len));
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_CHALLENGE);
- return RLM_MODULE_HANDLED;
- }
-
- return RLM_MODULE_HANDLED;
-}
-
-#define ACTION_SECTION(_out, _field, _verb, _name) \
-do { \
- CONF_SECTION *_tmp; \
- _tmp = cf_section_find(server_cs, _verb, _name); \
- if (_tmp) { \
- if (unlang_compile(_tmp, MOD_AUTHORIZE, NULL) < 0) return -1; \
- found = true; \
- } \
- if (_out) _out->_field = _tmp; \
-} while (0)
-
/** Compile virtual server sections
*
* Called twice, once when a server with an eap-aka namespace is found, and once
* EAP-AKA module will use to execute unlang code.
*
*/
-static int mod_section_compile(eap_aka_actions_t *actions, CONF_SECTION *server_cs)
+static int mod_section_compile(eap_aka_sim_actions_t *actions, CONF_SECTION *server_cs)
{
bool found = false;
if (!fr_cond_assert(server_cs)) return -1;
/*
- * Initial Identity-Response
- *
- * We then either:
- * - Request a new identity
- * - Start full authentication
- * - Start fast re-authentication
- * - Fail...
+ * Identity negotiation
*/
- ACTION_SECTION(actions, recv_eap_identity_response, "recv", "EAP-Identity-Response");
+ EAP_SECTION_COMPILE(actions, send_identity_request, "send", "Identity-Request");
+ EAP_SECTION_COMPILE(actions, recv_identity_response, "recv", "Identity-Response");
/*
- * Identity negotiation
+ * Optional override sections if the user *really*
+ * wants to apply special policies for subsequent
+ * request/response rounds.
*/
- ACTION_SECTION(actions, send_identity_request, "send", "Identity-Request");
- ACTION_SECTION(actions, recv_identity_response, "recv", "Identity-Response");
+ EAP_SECTION_COMPILE(actions, aka.send_aka_identity_request, "send", "AKA-Identity-Request");
+ EAP_SECTION_COMPILE(actions, aka.recv_aka_identity_response, "recv", "AKA-Identity-Response");
/*
* Full-Authentication
*/
- ACTION_SECTION(actions, send_challenge_request, "send", "Challenge-Request");
- ACTION_SECTION(actions, recv_challenge_response, "recv", "Challenge-Response");
+ EAP_SECTION_COMPILE(actions, send_challenge_request, "send", "Challenge-Request");
+ EAP_SECTION_COMPILE(actions, recv_challenge_response, "recv", "Challenge-Response");
/*
- * Fast-Re-Authentication
+ * Fast-Reauthentication
*/
- ACTION_SECTION(actions, send_fast_reauth_request, "send", "Fast-Reauth-Request");
- ACTION_SECTION(actions, recv_fast_reauth_response, "recv", "Fast-Reauth-Response");
+ EAP_SECTION_COMPILE(actions, send_reauthentication_request, "send", "Reauthentication-Request");
+ EAP_SECTION_COMPILE(actions, recv_reauthentication_response, "recv", "Reauthentication-Response");
/*
* Failures originating from the supplicant
*/
- ACTION_SECTION(actions, recv_client_error, "recv", "Client-Error");
- ACTION_SECTION(actions, recv_authentication_reject, "recv", "Authentication-Reject");
- ACTION_SECTION(actions, recv_syncronization_failure, "recv", "Syncronization-Failure");
+ EAP_SECTION_COMPILE(actions, recv_client_error, "recv", "Client-Error");
+ EAP_SECTION_COMPILE(actions, aka.recv_authentication_reject, "recv", "Authentication-Reject");
+ EAP_SECTION_COMPILE(actions, aka.recv_syncronization_failure, "recv", "Syncronization-Failure");
/*
* Failure originating from the server
*/
- ACTION_SECTION(actions, send_failure_notification, "send", "Failure-Notification");
- ACTION_SECTION(actions, recv_failure_notification_ack, "recv", "Failure-Notification-ACK");
+ EAP_SECTION_COMPILE(actions, send_failure_notification, "send", "Failure-Notification");
+ EAP_SECTION_COMPILE(actions, recv_failure_notification_ack, "recv", "Failure-Notification-ACK");
/*
* Protected success indication
*/
- ACTION_SECTION(actions, send_success_notification, "send", "Success-Notification");
- ACTION_SECTION(actions, recv_success_notification_ack, "recv", "Success-Notification-ACK");
+ EAP_SECTION_COMPILE(actions, send_success_notification, "send", "Success-Notification");
+ EAP_SECTION_COMPILE(actions, recv_success_notification_ack, "recv", "Success-Notification-ACK");
/*
* Final EAP-Success and EAP-Failure messages
*/
- ACTION_SECTION(actions, send_eap_success, "send", "EAP-Success");
- ACTION_SECTION(actions, send_eap_failure, "send", "EAP-Failure");
+ EAP_SECTION_COMPILE(actions, send_eap_success, "send", "EAP-Success");
+ EAP_SECTION_COMPILE(actions, send_eap_failure, "send", "EAP-Failure");
/*
* Fast-Reauth vectors
*/
- ACTION_SECTION(actions, load_session, "load", "session");
- ACTION_SECTION(actions, store_session, "store", "session");
- ACTION_SECTION(actions, clear_session, "clear", "session");
+ EAP_SECTION_COMPILE(actions, store_session, "store", "session");
+ EAP_SECTION_COMPILE(actions, load_session, "load", "session");
+ EAP_SECTION_COMPILE(actions, clear_session, "clear", "session");
+
+ /*
+ * Pseudonym processing
+ */
+ EAP_SECTION_COMPILE(actions, store_pseudonym, "store", "pseudonym");
+ EAP_SECTION_COMPILE(actions, load_pseudonym, "load", "pseudonym");
+ EAP_SECTION_COMPILE(actions, clear_pseudonym, "clear", "pseudonym");
/*
* Warn if we couldn't find any actions.
return 0;
}
+static int mod_instantiate(void *instance, CONF_SECTION *conf)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+
+ if (mod_section_compile(&inst->actions, inst->virtual_server) < 0) return -1;
+
+ /*
+ * If the user didn't specify a bidding value
+ * infer whether we need to send the bidding
+ * attribute, by whether the EAP module has
+ * has the AKA-Prime module enabled.
+ */
+ if (!inst->send_at_bidding_prefer_prime_is_set) {
+ CONF_SECTION *parent = cf_item_to_section(cf_parent(conf));
+ CONF_PAIR *cp = NULL;
+
+ while ((cp = cf_pair_find_next(parent, cp, "type"))) {
+ if (strcmp(cf_pair_value(cp), "aka-prime") == 0) {
+ cf_log_debug(conf, "Setting 'prefer_aka_prime = yes', as EAP-AKA-Prime is enabled");
+ inst->send_at_bidding_prefer_prime = true;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
/** Compile any virtual servers with the "eap-aka" namespace
*
*/
static int mod_load(void)
{
- if (virtual_namespace_register("eap-aka", "eap-aka", "eap/aka", mod_namespace_load) < 0) return -1;
+ if (virtual_namespace_register("eap-aka", "eap-aka-sim", "eap/aka-sim", mod_namespace_load) < 0) return -1;
- if (fr_sim_init() < 0) return -1;
+ if (fr_aka_sim_init() < 0) return -1;
- sim_xlat_register();
+ fr_aka_sim_xlat_register();
return 0;
}
static void mod_unload(void)
{
- sim_xlat_unregister();
+ fr_aka_sim_xlat_unregister();
- fr_sim_free();
+ fr_aka_sim_free();
}
/*
.name = "eap_aka",
.magic = RLM_MODULE_INIT,
- .provides = { FR_EAP_METHOD_AKA, FR_EAP_METHOD_AKA_PRIME },
- .inst_size = sizeof(rlm_eap_aka_t),
- .inst_type = "rlm_eap_aka_t",
+ .provides = { FR_EAP_METHOD_AKA },
+
+ .inst_size = sizeof(eap_aka_sim_common_conf_t),
+ .inst_type = "eap_aka_sim_common_conf_t",
.config = submodule_config,
.onload = mod_load,
.unload = mod_unload,
- .session_init = mod_session_init, /* Initialise a new EAP session */
- .entry_point = mod_process, /* Process next round of EAP method */
+
+ .instantiate = mod_instantiate,
+
+ .session_init = aka_sim_state_machine_start, /* Initialise a new EAP session */
+ .namespace = &dict_eap_aka_sim
};
--- /dev/null
+# rlm_eap_aka_prime
+## Metadata
+<dl>
+ <dt>category</dt><dd>authentication</dd>
+</dl>
+
+## Summary
+Implements [RFC 5448](https://tools.ietf.org/html/rfc5448) EAP-AKA authentication. EAP-AKA provides authentication
+and session keying material for 802.11i (WPA/2 Enterprise) using UMTS quintuplets.
--- /dev/null
+TARGETNAME := rlm_eap_aka_prime
+
+ifneq "$(OPENSSL_LIBS)" ""
+TARGET := $(TARGETNAME).a
+endif
+
+SOURCES := $(TARGETNAME).c
+
+TGT_PREREQS := $(LIBFREERADIUS_SERVER) libfreeradius-radius.a libfreeradius-util.a libfreeradius-eap.a libfreeradius-sim.a libfreeradius-eap-aka-sim.a
--- /dev/null
+/*
+ * This program is is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file rlm_eap_aka_prime.c
+ * @brief Implements EAP-AKA'
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ *
+ * @copyright 2019 The FreeRADIUS server project
+ * @copyright 2019 Network RADIUS SARL <sales@networkradius.com>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/eap/base.h>
+#include <freeradius-devel/eap_aka_sim/state_machine.h>
+#include <freeradius-devel/server/rad_assert.h>
+#include <freeradius-devel/unlang/compile.h>
+#include <freeradius-devel/unlang/module.h>
+
+static CONF_PARSER submodule_config[] = {
+ { FR_CONF_OFFSET("network_name", FR_TYPE_STRING, eap_aka_sim_common_conf_t, network_name ) },
+ { FR_CONF_OFFSET("request_identity", FR_TYPE_BOOL, eap_aka_sim_common_conf_t, request_identity ),
+ .func = cf_table_parse_uint32, .uctx = &(cf_table_parse_ctx_t){ .table = fr_aka_sim_id_request_table, .len = &fr_aka_sim_id_request_table_len }},
+ { FR_CONF_OFFSET("strip_permanent_identity_hint", FR_TYPE_BOOL, eap_aka_sim_common_conf_t,
+ strip_permanent_identity_hint ), .dflt = "yes" },
+ { FR_CONF_OFFSET("ephemeral_id_length", FR_TYPE_UINT8, eap_aka_sim_common_conf_t, ephemeral_id_length ), .dflt = "14" }, /* 14 for compatibility */
+ { FR_CONF_OFFSET("protected_success", FR_TYPE_BOOL, eap_aka_sim_common_conf_t, protected_success ), .dflt = "no" },
+ { FR_CONF_OFFSET("virtual_server", FR_TYPE_VOID, eap_aka_sim_common_conf_t, virtual_server), .func = virtual_server_cf_parse },
+ CONF_PARSER_TERMINATOR
+};
+
+static fr_dict_t *dict_eap_aka_sim;
+
+extern fr_dict_autoload_t rlm_eap_aka_dict[];
+fr_dict_autoload_t rlm_eap_aka_dict[] = {
+ { .out = &dict_eap_aka_sim, .base_dir="eap/aka-sim", .proto = "eap-aka-sim" },
+ { NULL }
+};
+
+/** Compile virtual server sections
+ *
+ * Called twice, once when a server with an eap-aka namespace is found, and once
+ * when an EAP-AKA module is instantiated.
+ *
+ * The first time is with actions == NULL and is to compile the sections and
+ * perform validation.
+ * The second time is to write out pointers to the compiled sections which the
+ * EAP-AKA module will use to execute unlang code.
+ *
+ */
+static int mod_section_compile(eap_aka_sim_actions_t *actions, CONF_SECTION *server_cs)
+{
+ bool found = false;
+
+ if (!fr_cond_assert(server_cs)) return -1;
+
+ /*
+ * Identity negotiation
+ */
+ EAP_SECTION_COMPILE(actions, send_identity_request, "send", "Identity-Request");
+ EAP_SECTION_COMPILE(actions, recv_identity_response, "recv", "Identity-Response");
+
+ /*
+ * Optional override sections if the user *really*
+ * wants to apply special policies for subsequent
+ * request/response rounds.
+ */
+ EAP_SECTION_COMPILE(actions, aka.send_aka_identity_request, "send", "AKA-Identity-Request");
+ EAP_SECTION_COMPILE(actions, aka.recv_aka_identity_response, "recv", "AKA-Identity-Response");
+
+ /*
+ * Full-Authentication
+ */
+ EAP_SECTION_COMPILE(actions, send_challenge_request, "send", "Challenge-Request");
+ EAP_SECTION_COMPILE(actions, recv_challenge_response, "recv", "Challenge-Response");
+
+ /*
+ * Fast-Re-Authentication
+ */
+ EAP_SECTION_COMPILE(actions, send_reauthentication_request, "send", "Reauthentication-Request");
+ EAP_SECTION_COMPILE(actions, recv_reauthentication_response, "recv", "Reauthentication-Response");
+
+ /*
+ * Failures originating from the supplicant
+ */
+ EAP_SECTION_COMPILE(actions, recv_client_error, "recv", "Client-Error");
+ EAP_SECTION_COMPILE(actions, aka.recv_authentication_reject, "recv", "Authentication-Reject");
+ EAP_SECTION_COMPILE(actions, aka.recv_syncronization_failure, "recv", "Syncronization-Failure");
+
+ /*
+ * Failure originating from the server
+ */
+ EAP_SECTION_COMPILE(actions, send_failure_notification, "send", "Failure-Notification");
+ EAP_SECTION_COMPILE(actions, recv_failure_notification_ack, "recv", "Failure-Notification-ACK");
+
+ /*
+ * Protected success indication
+ */
+ EAP_SECTION_COMPILE(actions, send_success_notification, "send", "Success-Notification");
+ EAP_SECTION_COMPILE(actions, recv_success_notification_ack, "recv", "Success-Notification-ACK");
+
+ /*
+ * Final EAP-Success and EAP-Failure messages
+ */
+ EAP_SECTION_COMPILE(actions, send_eap_success, "send", "EAP-Success");
+ EAP_SECTION_COMPILE(actions, send_eap_failure, "send", "EAP-Failure");
+
+ /*
+ * Fast-Reauth vectors
+ */
+ EAP_SECTION_COMPILE(actions, store_session, "store", "session");
+ EAP_SECTION_COMPILE(actions, load_session, "load", "session");
+ EAP_SECTION_COMPILE(actions, clear_session, "clear", "session");
+
+ /*
+ * Pseudonym processing
+ */
+ EAP_SECTION_COMPILE(actions, store_pseudonym, "store", "pseudonym");
+ EAP_SECTION_COMPILE(actions, load_pseudonym, "load", "pseudonym");
+ EAP_SECTION_COMPILE(actions, clear_pseudonym, "clear", "pseudonym");
+
+ /*
+ * Warn if we couldn't find any actions.
+ */
+ if (!found) {
+ cf_log_warn(server_cs, "No \"eap-aka-prime\" actions found in virtual server \"%s\"",
+ cf_section_name2(server_cs));
+ }
+
+ return 0;
+}
+
+static int mod_instantiate(void *instance, UNUSED CONF_SECTION *conf)
+{
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
+
+ if (mod_section_compile(&inst->actions, inst->virtual_server) < 0) return -1;
+
+ return 0;
+}
+
+/** Compile any virtual servers with the "eap-aka" namespace
+ *
+ */
+static int mod_namespace_load(CONF_SECTION *server_cs)
+{
+ return mod_section_compile(NULL, server_cs);
+}
+
+static int mod_load(void)
+{
+ if (virtual_namespace_register("eap-aka-prime", "eap-aka-sim", "eap/aka-sim", mod_namespace_load) < 0) return -1;
+
+ if (fr_aka_sim_init() < 0) return -1;
+
+ fr_aka_sim_xlat_register();
+
+ return 0;
+}
+
+static void mod_unload(void)
+{
+ fr_aka_sim_xlat_unregister();
+
+ fr_aka_sim_free();
+}
+
+/*
+ * The module name should be the only globally exported symbol.
+ * That is, everything else should be 'static'.
+ */
+extern rlm_eap_submodule_t rlm_eap_aka_prime;
+rlm_eap_submodule_t rlm_eap_aka_prime = {
+ .name = "eap_aka_prime",
+ .magic = RLM_MODULE_INIT,
+
+ .provides = { FR_EAP_METHOD_AKA_PRIME },
+
+ .inst_size = sizeof(eap_aka_sim_common_conf_t),
+ .inst_type = "eap_aka_sim_common_conf_t",
+ .config = submodule_config,
+
+ .onload = mod_load,
+ .unload = mod_unload,
+
+ .instantiate = mod_instantiate,
+
+ .session_init = aka_sim_state_machine_start, /* Initialise a new EAP session */
+ .namespace = &dict_eap_aka_sim
+};
SOURCES := $(TARGETNAME).c
-SRC_CFLAGS :=
-TGT_LDLIBS :=
-SRC_INCDIRS := ${top_srcdir}/src/modules/rlm_eap/ ${top_srcdir}/src/modules/rlm_eap/lib/base/ ${top_srcdir}/src/modules/rlm_eap/lib/sim/
-
-TGT_PREREQS := $(LIBFREERADIUS_SERVER) libfreeradius-radius.a libfreeradius-util.a libfreeradius-eap.a libfreeradius-eap-sim.a
+TGT_PREREQS := $(LIBFREERADIUS_SERVER) libfreeradius-radius.a libfreeradius-util.a libfreeradius-eap.a libfreeradius-sim.a libfreeradius-eap-aka-sim.a
/**
* $Id$
- * @file rlm_eap_sim.c
- * @brief Implements the SIM part of EAP-SIM
+ * @file rlm_eap_aka.c
+ * @brief Implements EAP-AKA
*
- * The development of the EAP/SIM support was funded by Internet Foundation
- * Austria (http://www.nic.at/ipa).
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
*
- * @copyright 2003 Michael Richardson (mcr@sandelman.ottawa.on.ca)
- * @copyright 2003-2016 The FreeRADIUS server project
- * @copyright 2016 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ * @copyright 2016 The FreeRADIUS server project
+ * @copyright 2016 Network RADIUS SARL <sales@networkradius.com>
*/
RCSID("$Id$")
#include <freeradius-devel/eap/base.h>
-#include <freeradius-devel/eap/types.h>
+#include <freeradius-devel/eap_aka_sim/state_machine.h>
#include <freeradius-devel/server/rad_assert.h>
-#include <freeradius-devel/sim/base.h>
-
-#include <freeradius-devel/protocol/eap/sim/rfc4187.h>
-
-#include "eap_sim.h"
-
-#ifndef EAP_TLS_MPPE_KEY_LEN
-# define EAP_TLS_MPPE_KEY_LEN 32
-#endif
-
-static fr_table_num_ordered_t const sim_state_table[] = {
- { "START", EAP_SIM_SERVER_START },
- { "CHALLENGE", EAP_SIM_SERVER_CHALLENGE },
- { "REAUTHENTICATE", EAP_SIM_SERVER_REAUTHENTICATE },
- { "SUCCESS-NOTIFICATION", EAP_SIM_SERVER_SUCCESS_NOTIFICATION },
- { "SUCCESS", EAP_SIM_SERVER_SUCCESS },
- { "FAILURE-NOTIFICATION", EAP_SIM_SERVER_FAILURE_NOTIFICATION },
- { "FAILURE", EAP_SIM_SERVER_FAILURE },
-};
-static size_t sim_state_table_len = NUM_ELEMENTS(sim_state_table);
+#include <freeradius-devel/unlang/compile.h>
+#include <freeradius-devel/unlang/module.h>
static CONF_PARSER submodule_config[] = {
- { FR_CONF_OFFSET("virtual_server", FR_TYPE_STRING, rlm_eap_sim_t, virtual_server) },
- { FR_CONF_OFFSET("protected_success", FR_TYPE_BOOL, rlm_eap_sim_t, protected_success ), .dflt = "no" },
+ { FR_CONF_OFFSET("request_identity", FR_TYPE_BOOL, eap_aka_sim_common_conf_t, request_identity ),
+ .func = cf_table_parse_uint32, .uctx = &(cf_table_parse_ctx_t){ .table = fr_aka_sim_id_request_table, .len = &fr_aka_sim_id_request_table_len }},
+ { FR_CONF_OFFSET("strip_permanent_identity_hint", FR_TYPE_BOOL, eap_aka_sim_common_conf_t,
+ strip_permanent_identity_hint ), .dflt = "yes" },
+ { FR_CONF_OFFSET("ephemeral_id_length", FR_TYPE_UINT8, eap_aka_sim_common_conf_t, ephemeral_id_length ), .dflt = "14" }, /* 14 for compatibility */
+ { FR_CONF_OFFSET("protected_success", FR_TYPE_BOOL, eap_aka_sim_common_conf_t, protected_success ), .dflt = "no" },
+ { FR_CONF_OFFSET("virtual_server", FR_TYPE_VOID | FR_TYPE_REQUIRED, eap_aka_sim_common_conf_t, virtual_server), .func = virtual_server_cf_parse },
CONF_PARSER_TERMINATOR
};
-static fr_dict_t *dict_freeradius;
-static fr_dict_t *dict_radius;
-static fr_dict_t *dict_eap_sim;
-
-extern fr_dict_autoload_t rlm_eap_sim_dict[];
-fr_dict_autoload_t rlm_eap_sim_dict[] = {
- { .out = &dict_freeradius, .proto = "freeradius" },
- { .out = &dict_radius, .proto = "radius" },
- { .out = &dict_eap_sim, .proto = "eap-sim", .base_dir = "eap/sim" },
- { NULL }
-};
-
-static fr_dict_attr_t const *attr_eap_sim_mk;
-static fr_dict_attr_t const *attr_eap_sim_subtype;
-
-static fr_dict_attr_t const *attr_ms_mppe_send_key;
-static fr_dict_attr_t const *attr_ms_mppe_recv_key;
+static fr_dict_t *dict_eap_aka_sim;
-static fr_dict_attr_t const *attr_eap_sim_any_id_req;
-static fr_dict_attr_t const *attr_eap_sim_client_error_code;
-static fr_dict_attr_t const *attr_eap_sim_counter;
-static fr_dict_attr_t const *attr_eap_sim_encr_data;
-static fr_dict_attr_t const *attr_eap_sim_fullauth_id_req;
-static fr_dict_attr_t const *attr_eap_sim_identity;
-static fr_dict_attr_t const *attr_eap_sim_mac;
-static fr_dict_attr_t const *attr_eap_sim_nonce_mt;
-static fr_dict_attr_t const *attr_eap_sim_nonce_s;
-static fr_dict_attr_t const *attr_eap_sim_notification;
-static fr_dict_attr_t const *attr_eap_sim_permanent_id_req;
-static fr_dict_attr_t const *attr_eap_sim_rand;
-static fr_dict_attr_t const *attr_eap_sim_result_ind;
-static fr_dict_attr_t const *attr_eap_sim_selected_version;
-static fr_dict_attr_t const *attr_eap_sim_version_list;
-
-extern fr_dict_attr_autoload_t rlm_eap_sim_dict_attr[];
-fr_dict_attr_autoload_t rlm_eap_sim_dict_attr[] = {
- { .out = &attr_eap_sim_mk, .name = "EAP-SIM-MK", .type = FR_TYPE_OCTETS, .dict = &dict_eap_sim },
-
- { .out = &attr_ms_mppe_send_key, .name = "MS-MPPE-Send-Key", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
- { .out = &attr_ms_mppe_recv_key, .name = "MS-MPPE-Recv-Key", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
-
- { .out = &attr_eap_sim_any_id_req, .name = "EAP-SIM-Any-ID-Req", .type = FR_TYPE_BOOL, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_client_error_code, .name = "EAP-SIM-Client-Error-Code", .type = FR_TYPE_UINT16, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_counter, .name = "EAP-SIM-Counter", .type = FR_TYPE_UINT16, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_encr_data, .name = "EAP-SIM-Encr-Data", .type = FR_TYPE_TLV, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_fullauth_id_req, .name = "EAP-SIM-Fullauth-ID-Req", .type = FR_TYPE_BOOL, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_identity, .name = "EAP-SIM-Identity", .type = FR_TYPE_STRING, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_mac, .name = "EAP-SIM-MAC", .type = FR_TYPE_OCTETS, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_nonce_mt, .name = "EAP-SIM-Nonce-MT", .type = FR_TYPE_OCTETS, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_nonce_s, .name = "EAP-SIM-Nonce-S", .type = FR_TYPE_OCTETS, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_notification, .name = "EAP-SIM-Notification", .type = FR_TYPE_UINT16, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_permanent_id_req, .name = "EAP-SIM-Permanent-ID-Req", .type = FR_TYPE_BOOL, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_rand, .name = "EAP-SIM-RAND", .type = FR_TYPE_OCTETS, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_result_ind, .name = "EAP-SIM-Result-Ind", .type = FR_TYPE_BOOL, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_selected_version, .name = "EAP-SIM-Selected-Version", .type = FR_TYPE_UINT16, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_subtype, .name = "EAP-SIM-Subtype", .type = FR_TYPE_UINT32, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_version_list, .name = "EAP-SIM-Version-List", .type = FR_TYPE_UINT16, .dict = &dict_eap_sim },
+extern fr_dict_autoload_t rlm_eap_aka_dict[];
+fr_dict_autoload_t rlm_eap_aka_dict[] = {
+ { .out = &dict_eap_aka_sim, .base_dir="eap/aka-sim", .proto = "eap-aka-sim" },
{ NULL }
};
-/*
- * build a reply to be sent.
- */
-static int eap_sim_compose(eap_session_t *eap_session, uint8_t const *hmac_extra, size_t hmac_extra_len)
-{
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
- fr_cursor_t cursor;
- fr_cursor_t to_encode;
- VALUE_PAIR *head = NULL, *vp;
- REQUEST *request = eap_session->request;
- fr_sim_encode_ctx_t encoder_ctx = {
- .root = fr_dict_root(dict_eap_sim),
- .keys = &eap_sim_session->keys,
-
- .iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
- .iv_included = false,
-
- .hmac_md = EVP_sha1(),
- .eap_packet = eap_session->this_round->request,
- .hmac_extra = hmac_extra,
- .hmac_extra_len = hmac_extra_len
- };
-
- ssize_t ret;
-
- /* we will set the ID on requests, since we have to HMAC it */
- eap_session->this_round->set_request_id = true;
-
- fr_cursor_init(&cursor, &eap_session->request->reply->vps);
- fr_cursor_init(&to_encode, &head);
-
- while ((vp = fr_cursor_current(&cursor))) {
- if (!fr_dict_parent_common(fr_dict_root(dict_eap_sim), vp->da, true)) {
- fr_cursor_next(&cursor);
- continue;
- }
- vp = fr_cursor_remove(&cursor);
-
- /*
- * Silently discard encrypted attributes until
- * the peer should have k_encr. These can be
- * added by policy, and seem to cause
- * wpa_supplicant to fail if sent before the challenge.
- */
- if (!eap_sim_session->allow_encrypted && fr_dict_parent_common(attr_eap_sim_encr_data, vp->da, true)) {
- RWDEBUG("Silently discarding &reply:%s: Encrypted attributes not allowed in this round",
- vp->da->name);
- talloc_free(vp);
- continue;
- }
-
- fr_cursor_append(&to_encode, vp);
- }
-
- RDEBUG2("Encoding EAP-SIM attributes");
- log_request_pair_list(L_DBG_LVL_2, request, head, NULL);
-
- eap_session->this_round->request->type.num = FR_EAP_METHOD_SIM;
- eap_session->this_round->request->id = eap_sim_session->sim_id++ & 0xff;
- eap_session->this_round->set_request_id = true;
-
- ret = fr_sim_encode(eap_session->request, head, &encoder_ctx);
- fr_cursor_head(&to_encode);
- fr_cursor_free_list(&to_encode);
-
- if (ret < 0) {
- RPEDEBUG("Failed encoding EAP-SIM data");
- return -1;
- }
- return 0;
-}
-
-static int eap_sim_send_start(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- VALUE_PAIR **vps, *vp;
- uint16_t version;
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
- RADIUS_PACKET *packet;
-
- rad_assert(eap_session->request != NULL);
- rad_assert(eap_session->request->reply);
-
- RDEBUG2("Sending SIM-State");
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
- eap_sim_session->allow_encrypted = false; /* In case this is after failed fast-resumption */
-
- /* these are the outgoing attributes */
- packet = eap_session->request->reply;
- vps = &packet->vps;
- rad_assert(vps != NULL);
-
- /*
- * Add appropriate TLVs for the EAP things we wish to send.
- */
- vp = fr_pair_afrom_da(packet, attr_eap_sim_version_list);
- vp->vp_uint16 = EAP_SIM_VERSION;
- fr_pair_add(vps, vp);
-
- /* record it in the ess */
- version = htons(EAP_SIM_VERSION);
- memcpy(eap_sim_session->keys.gsm.version_list, &version, sizeof(version));
- eap_sim_session->keys.gsm.version_list_len = 2;
-
- /*
- * Select the right type of identity request attribute
- */
- switch (eap_sim_session->id_req) {
- case SIM_ANY_ID_REQ:
- vp = fr_pair_afrom_da(packet, attr_eap_sim_any_id_req);
- break;
-
- case SIM_PERMANENT_ID_REQ:
- vp = fr_pair_afrom_da(packet, attr_eap_sim_permanent_id_req);
- break;
-
- case SIM_FULLAUTH_ID_REQ:
- vp = fr_pair_afrom_da(packet, attr_eap_sim_fullauth_id_req);
- break;
-
- default:
- rad_assert(0);
- }
- vp->vp_bool = true;
- fr_pair_replace(vps, vp);
-
- /* the SUBTYPE, set to start. */
- vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
- vp->vp_uint16 = EAP_SIM_START;
- fr_pair_replace(vps, vp);
-
- /*
- * Encode the packet
- */
- if (eap_sim_compose(eap_session, NULL, 0) < 0) {
- fr_pair_list_free(&packet->vps);
- return -1;
- }
-
- return 0;
-}
-
-/** Send the challenge itself
- *
- * Challenges will come from one of three places eventually:
- *
- * 1 from attributes like FR_EAP_SIM_RANDx
- * (these might be retrieved from a database)
- *
- * 2 from internally implemented SIM authenticators
- * (a simple one based upon XOR will be provided)
- *
- * 3 from some kind of SS7 interface.
- *
- * For now, they only come from attributes.
- * It might be that the best way to do 2/3 will be with a different
- * module to generate/calculate things.
- */
-static int eap_sim_send_challenge(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
- VALUE_PAIR **to_peer, *vp;
- RADIUS_PACKET *packet;
- fr_sim_vector_src_t src = SIM_VECTOR_SRC_AUTO;
-
- rad_assert(eap_session->request != NULL);
- rad_assert(eap_session->request->reply);
-
- RDEBUG2("Acquiring GSM vector(s)");
- if ((fr_sim_vector_gsm_from_attrs(eap_session, request->control, 0, &eap_sim_session->keys, &src) != 0) ||
- (fr_sim_vector_gsm_from_attrs(eap_session, request->control, 1, &eap_sim_session->keys, &src) != 0) ||
- (fr_sim_vector_gsm_from_attrs(eap_session, request->control, 2, &eap_sim_session->keys, &src) != 0)) {
- REDEBUG("Failed retrieving SIM vectors");
- return RLM_MODULE_FAIL;
- }
-
- /*
- * All set, calculate keys!
- */
- fr_sim_crypto_kdf_0_gsm(&eap_sim_session->keys);
- if (RDEBUG_ENABLED3) fr_sim_crypto_keys_log(request, &eap_sim_session->keys);
-
- RDEBUG2("Sending SIM-Challenge");
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
-
- /*
- * to_peer is the data to the client
- */
- packet = eap_session->request->reply;
- to_peer = &packet->vps;
-
- /*
- * Okay, we got the challenges! Put them into attributes.
- */
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_rand));
- fr_pair_value_memcpy(vp, eap_sim_session->keys.gsm.vector[0].rand, SIM_VECTOR_GSM_RAND_SIZE, false);
- fr_pair_add(to_peer, vp);
-
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_rand));
- fr_pair_value_memcpy(vp, eap_sim_session->keys.gsm.vector[1].rand, SIM_VECTOR_GSM_RAND_SIZE, false);
- fr_pair_add(to_peer, vp);
-
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_rand));
- fr_pair_value_memcpy(vp, eap_sim_session->keys.gsm.vector[2].rand, SIM_VECTOR_GSM_RAND_SIZE, false);
- fr_pair_add(to_peer, vp);
-
- /*
- * Set subtype to challenge.
- */
- vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
- vp->vp_uint16 = EAP_SIM_CHALLENGE;
- fr_pair_replace(to_peer, vp);
-
- /*
- * Indicate we'd like to use protected success messages
- */
- if (eap_sim_session->send_result_ind) {
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_result_ind));
- vp->vp_bool = true;
- fr_pair_replace(to_peer, vp);
- }
-
- /*
- * Need to include an AT_MAC attribute so that it will get
- * calculated.
- */
- vp = fr_pair_afrom_da(packet, attr_eap_sim_mac);
- fr_pair_replace(to_peer, vp);
-
- /*
- * We've sent the challenge so the peer should now be able
- * to accept encrypted attributes.
- */
- eap_sim_session->allow_encrypted = true;
-
- /*
- * Encode the packet
- */
- if (eap_sim_compose(eap_session,
- eap_sim_session->keys.gsm.nonce_mt, sizeof(eap_sim_session->keys.gsm.nonce_mt)) < 0) {
- fr_pair_list_free(&packet->vps);
- return -1;
- }
-
- return 0;
-}
-
-/** Send NONCE_S and re-key
- *
- */
-static int eap_sim_send_reauthentication(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
- VALUE_PAIR **to_peer, *vp, *mk, *counter;
- RADIUS_PACKET *packet;
-
- rad_assert(eap_session->request != NULL);
- rad_assert(eap_session->request->reply);
-
- /*
- * to_peer is the data to the client
- */
- packet = eap_session->request->reply;
- to_peer = &packet->vps;
-
- /*
- * If any of the session resumption inputs (on our side)
- * are missing or malformed, return an error code
- * and the state machine will jump to the start state.
- */
- mk = fr_pair_find_by_da(request->control, attr_eap_sim_mk, TAG_ANY);
- if (!mk) {
- RWDEBUG2("Missing &control:EAP-SIM-MK, skipping session resumption");
- return -1;
- }
- if (mk->vp_length != SIM_MK_SIZE) {
- RWDEBUG("&control:EAP-SIM-MK has incorrect length, expected %u bytes got %zu bytes",
- SIM_MK_SIZE, mk->vp_length);
- return -1;
- }
- counter = fr_pair_find_by_da(request->control, attr_eap_sim_counter, TAG_ANY);
- if (!counter) {
- RWDEBUG2("Missing &control:EAP-SIM-Counter, skipping session resumption");
- return -1;
- }
-
- /*
- * All set, calculate keys!
- */
- fr_sim_crypto_keys_init_reauth(&eap_sim_session->keys, mk->vp_octets, counter->vp_uint16);
- fr_sim_crypto_kdf_0_reauth(&eap_sim_session->keys);
- if (RDEBUG_ENABLED3) fr_sim_crypto_keys_log(request, &eap_sim_session->keys);
-
- RDEBUG2("Sending SIM-Reauthentication");
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
-
- /*
- * Set subtype to challenge.
- */
- vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
- vp->vp_uint16 = EAP_SIM_REAUTH;
- fr_pair_replace(to_peer, vp);
-
- /*
- * Add nonce_s
- */
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_nonce_s));
- fr_pair_value_memcpy(vp, eap_sim_session->keys.reauth.nonce_s,
- sizeof(eap_sim_session->keys.reauth.nonce_s), false);
- fr_pair_replace(to_peer, vp);
-
- /*
- * Indicate we'd like to use protected success messages
- */
- if (eap_sim_session->send_result_ind) {
- MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_result_ind));
- vp->vp_bool = true;
- fr_pair_replace(to_peer, vp);
- }
-
- /*
- * Need to include an AT_MAC attribute so that it will get
- * calculated.
- */
- vp = fr_pair_afrom_da(packet, attr_eap_sim_mac);
- fr_pair_replace(to_peer, vp);
-
- /*
- * We've sent the challenge so the peer should now be able
- * to accept encrypted attributes.
- */
- eap_sim_session->allow_encrypted = true;
-
- /*
- * Encode the packet
- */
- if (eap_sim_compose(eap_session, NULL, 0) < 0) {
- fr_pair_list_free(&packet->vps);
- return -1;
- }
-
- return 0;
-}
-
-/** Send a success notification
- *
- */
-static int eap_sim_send_eap_success_notification(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- RADIUS_PACKET *packet = eap_session->request->reply;
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
- fr_cursor_t cursor;
- VALUE_PAIR *vp;
-
- RDEBUG2("Sending SIM-Notification (Success)");
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
-
- if (!fr_cond_assert(eap_sim_session->challenge_success)) return -1;
-
- fr_cursor_init(&cursor, &packet->vps);
-
- /*
- * Set the subtype to notification
- */
- vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
- vp->vp_uint16 = FR_EAP_SIM_SUBTYPE_VALUE_SIM_NOTIFICATION;
- fr_cursor_append(&cursor, vp);
-
- vp = fr_pair_afrom_da(packet, attr_eap_sim_notification);
- vp->vp_uint16 = FR_EAP_SIM_NOTIFICATION_VALUE_SUCCESS;
- fr_cursor_append(&cursor, vp);
-
- /*
- * Need to include an AT_MAC attribute so that it will get
- * calculated.
- */
- vp = fr_pair_afrom_da(packet, attr_eap_sim_mac);
- fr_pair_replace(&packet->vps, vp);
-
- /*
- * Encode the packet
- */
- if (eap_sim_compose(eap_session, NULL, 0) < 0) {
- fr_pair_list_free(&packet->vps);
- return -1;
- }
-
- return 0;
-}
-
-/** Send a success message
+/** Compile virtual server sections
*
- * The only work to be done is the add the appropriate SEND/RECV
- * radius attributes derived from the MSK.
- */
-static int eap_sim_send_eap_success(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- uint8_t *p;
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
-
- RDEBUG2("Sending SIM-Success");
- eap_session->this_round->request->code = FR_EAP_CODE_SUCCESS;
- eap_session->finished = true;
-
- p = eap_sim_session->keys.msk;
- eap_add_reply(eap_session->request, attr_ms_mppe_recv_key, p, EAP_TLS_MPPE_KEY_LEN);
- p += EAP_TLS_MPPE_KEY_LEN;
- eap_add_reply(eap_session->request, attr_ms_mppe_send_key, p, EAP_TLS_MPPE_KEY_LEN);
-
- return 0;
-}
-
-/** Send a failure message
+ * Called twice, once when a server with an eap-aka namespace is found, and once
+ * when an EAP-AKA module is instantiated.
*
- */
-static int eap_sim_send_eap_failure_notification(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
- RADIUS_PACKET *packet = eap_session->request->reply;
- fr_cursor_t cursor;
- VALUE_PAIR *vp;
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
-
- fr_cursor_init(&cursor, &packet->vps);
-
- vp = fr_pair_find_by_da(packet->vps, attr_eap_sim_notification, TAG_ANY);
- if (!vp) {
- vp = fr_pair_afrom_da(packet, attr_eap_sim_notification);
- vp->vp_uint16 = FR_EAP_SIM_NOTIFICATION_VALUE_GENERAL_FAILURE;
- fr_cursor_append(&cursor, vp);
- }
-
- /*
- * Change the failure notification depending where
- * we are in the state machine.
- */
- if (eap_sim_session->challenge_success) {
- vp->vp_uint16 &= ~0x4000; /* Unset phase bit */
- } else {
- vp->vp_uint16 |= 0x4000; /* Set phase bit */
- }
- vp->vp_uint16 &= ~0x8000; /* In both cases success bit should be low */
-
- RDEBUG2("Sending SIM-Notification (%pV)", &vp->data);
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
-
- /*
- * Set the subtype to notification
- */
- vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype);
- vp->vp_uint16 = FR_EAP_SIM_SUBTYPE_VALUE_SIM_NOTIFICATION;
- fr_cursor_append(&cursor, vp);
-
- /*
- * If we're after the challenge phase
- * then we need to include a MAC to
- * protect notifications.
- */
- if (eap_sim_session->challenge_success) {
- vp = fr_pair_afrom_da(packet, attr_eap_sim_mac);
- fr_pair_replace(&packet->vps, vp);
- }
-
- /*
- * Encode the packet
- */
- if (eap_sim_compose(eap_session, NULL, 0) < 0) {
- fr_pair_list_free(&packet->vps);
- return -1;
- }
-
- return 0;
-}
-
-static int eap_sim_send_eap_failure(eap_session_t *eap_session)
-{
- REQUEST *request = eap_session->request;
-
- RDEBUG2("Sending EAP-Failure");
-
- eap_session->this_round->request->code = FR_EAP_CODE_FAILURE;
- eap_session->finished = true;
-
- return 0;
-}
-
-/** Run the server state machine
+ * The first time is with actions == NULL and is to compile the sections and
+ * perform validation.
+ * The second time is to write out pointers to the compiled sections which the
+ * EAP-AKA module will use to execute unlang code.
*
*/
-static void eap_sim_state_enter(eap_session_t *eap_session, eap_sim_server_state_t new_state)
+static int mod_section_compile(eap_aka_sim_actions_t *actions, CONF_SECTION *server_cs)
{
- REQUEST *request = eap_session->request;
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
+ bool found = false;
- if (new_state != eap_sim_session->state) {
- RDEBUG2("Changed state %s -> %s",
- fr_table_str_by_value(sim_state_table, eap_sim_session->state, "<unknown>"),
- fr_table_str_by_value(sim_state_table, new_state, "<unknown>"));
- eap_sim_session->state = new_state;
- } else {
- RDEBUG2("Reentering state %s",
- fr_table_str_by_value(sim_state_table, eap_sim_session->state, "<unknown>"));
- }
-
- switch (new_state) {
- /*
- * Send our version list
- */
- case EAP_SIM_SERVER_START:
- start:
- if (eap_sim_send_start(eap_session) < 0) {
- notify_failure:
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- return;
- }
- break;
+ if (!fr_cond_assert(server_cs)) return -1;
/*
- * Send the EAP-SIM Challenge message.
+ * Identity negotiation
*/
- case EAP_SIM_SERVER_CHALLENGE:
- if (eap_sim_send_challenge(eap_session) < 0) goto notify_failure;
- break;
-
- case EAP_SIM_SERVER_REAUTHENTICATE:
- if (eap_sim_send_reauthentication(eap_session) < 0) goto start;
- break;
+ EAP_SECTION_COMPILE(actions, send_identity_request, "send", "Identity-Request");
+ EAP_SECTION_COMPILE(actions, recv_identity_response, "recv", "Identity-Response");
/*
- * Sent a protected success notification
+ * Optional override sections if the user *really*
+ * wants to apply special policies for subsequent
+ * request/response rounds.
*/
- case EAP_SIM_SERVER_SUCCESS_NOTIFICATION:
- if (eap_sim_send_eap_success_notification(eap_session) < 0) goto notify_failure;
- break;
+ EAP_SECTION_COMPILE(actions, sim.send_sim_start_request, "send", "Start");
+ EAP_SECTION_COMPILE(actions, sim.recv_sim_start_response, "recv", "Start");
/*
- * Send the EAP Success message (we're done)
+ * Full-Authentication
*/
- case EAP_SIM_SERVER_SUCCESS:
- if (eap_sim_send_eap_success(eap_session) < 0) goto notify_failure;
- return;
+ EAP_SECTION_COMPILE(actions, send_challenge_request, "send", "Challenge-Request");
+ EAP_SECTION_COMPILE(actions, recv_challenge_response, "recv", "Challenge-Response");
/*
- * Send a general failure notification
+ * Fast-Re-Authentication
*/
- case EAP_SIM_SERVER_FAILURE_NOTIFICATION:
- if (eap_sim_send_eap_failure_notification(eap_session) < 0) { /* Fallback to EAP-Failure */
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
- }
- return;
+ EAP_SECTION_COMPILE(actions, send_reauthentication_request, "send", "Reauthentication-Request");
+ EAP_SECTION_COMPILE(actions, recv_reauthentication_response, "recv", "Reauthentication-Response");
/*
- * Send an EAP-Failure (we're done)
+ * Failures originating from the supplicant
*/
- case EAP_SIM_SERVER_FAILURE:
- eap_sim_send_eap_failure(eap_session);
- return;
-
- default:
- rad_assert(0); /* Invalid transition */
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- return;
- }
-}
-
-/** Process an EAP-Sim/Response/Start
- *
- * Verify that client chose a version, and provided a NONCE_MT,
- * and if so, then change states to challenge, and send the new
- * challenge, else, resend the Request/Start.
- */
-static int process_eap_sim_start(eap_session_t *eap_session, VALUE_PAIR *vps)
-{
- REQUEST *request = eap_session->request;
- VALUE_PAIR *nonce_vp, *selected_version_vp;
- eap_sim_session_t *eap_sim_session;
- uint16_t eap_sim_version;
- VALUE_PAIR *id;
- fr_sim_id_type_t type = SIM_ID_TYPE_UNKNOWN;
- fr_sim_method_hint_t method = SIM_METHOD_HINT_UNKNOWN;
-
- eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
+ EAP_SECTION_COMPILE(actions, recv_client_error, "recv", "Client-Error");
/*
- * For fullauth We require both the NONCE_MT
- * and SELECTED_VERSION from the peer, else
- * the packet is invalid.
+ * Failure originating from the server
*/
- nonce_vp = fr_pair_find_by_da(vps, attr_eap_sim_nonce_mt, TAG_ANY);
- selected_version_vp = fr_pair_find_by_da(vps, attr_eap_sim_selected_version, TAG_ANY);
- if (!nonce_vp || !selected_version_vp) {
- REDEBUG2("Client did not select a version and send a NONCE");
- return -1;
- }
-
- eap_sim_version = selected_version_vp->vp_uint16;
- if (eap_sim_version != EAP_SIM_VERSION) {
- REDEBUG2("EAP-SIM-Version %i is unknown", eap_sim_version);
- return -1;
- }
+ EAP_SECTION_COMPILE(actions, send_failure_notification, "send", "Failure-Notification");
+ EAP_SECTION_COMPILE(actions, recv_failure_notification_ack, "recv", "Failure-Notification-ACK");
/*
- * Record it for later keying
+ * Protected success indication
*/
- eap_sim_version = htons(eap_sim_version);
- memcpy(eap_sim_session->keys.gsm.version_select,
- &eap_sim_version, sizeof(eap_sim_session->keys.gsm.version_select));
+ EAP_SECTION_COMPILE(actions, send_success_notification, "send", "Success-Notification");
+ EAP_SECTION_COMPILE(actions, recv_success_notification_ack, "recv", "Success-Notification-ACK");
/*
- * Double check the nonce size.
+ * Final EAP-Success and EAP-Failure messages
*/
- if (nonce_vp->vp_length != 16) {
- REDEBUG("EAP-SIM nonce_mt must be 16 bytes, not %zu bytes", nonce_vp->vp_length);
- return -1;
- }
- memcpy(eap_sim_session->keys.gsm.nonce_mt, nonce_vp->vp_octets, 16);
+ EAP_SECTION_COMPILE(actions, send_eap_success, "send", "EAP-Success");
+ EAP_SECTION_COMPILE(actions, send_eap_failure, "send", "EAP-Failure");
/*
- * See if we got an AT_IDENTITY
+ * Fast-Reauth vectors
*/
- id = fr_pair_find_by_da(vps, attr_eap_sim_identity, TAG_ANY);
- if (id) {
- if (fr_sim_id_type(&type, &method,
- eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) {
- RPWDEBUG2("Failed parsing identity");
- }
-
- /*
- * Update cryptographic identity
- */
- talloc_const_free(eap_sim_session->keys.identity);
- eap_sim_session->keys.identity_len = id->vp_length;
- MEM(eap_sim_session->keys.identity = talloc_memdup(eap_sim_session, id->vp_strvalue, id->vp_length));
- }
+ EAP_SECTION_COMPILE(actions, store_session, "store", "session");
+ EAP_SECTION_COMPILE(actions, load_session, "load", "session");
+ EAP_SECTION_COMPILE(actions, clear_session, "clear", "session");
/*
- * @TODO Run a virtual server to see if we can use the
- * identity we just acquired, or whether we need to
- * negotiate the next permissive ID.
+ * Pseudonym processing
*/
+ EAP_SECTION_COMPILE(actions, store_pseudonym, "store", "pseudonym");
+ EAP_SECTION_COMPILE(actions, load_pseudonym, "load", "pseudonym");
+ EAP_SECTION_COMPILE(actions, clear_pseudonym, "clear", "pseudonym");
/*
- * Negotiate the next permissive form
- * if identity, or fail.
+ * Warn if we couldn't find any actions.
*/
- switch (eap_sim_session->id_req) {
- case SIM_ANY_ID_REQ:
- eap_sim_session->id_req = SIM_FULLAUTH_ID_REQ;
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_START);
- break;
-
- case SIM_FULLAUTH_ID_REQ:
- eap_sim_session->id_req = SIM_PERMANENT_ID_REQ;
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_START);
- break;
-
- case SIM_NO_ID_REQ:
- case SIM_PERMANENT_ID_REQ:
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_CHALLENGE);
-// REDEBUG2("Failed to negotiate a usable identity");
-// eap_sim_state_enter(eap_session, eap_sim_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- break;
+ if (!found) {
+ cf_log_warn(server_cs, "No \"eap-aka\" actions found in virtual server \"%s\"",
+ cf_section_name2(server_cs));
}
return 0;
}
-
-/** Process an EAP-Sim/Response/Challenge
- *
- * Verify that MAC that we received matches what we would have
- * calculated from the packet with the SRESx appended.
- */
-static int process_eap_sim_challenge(eap_session_t *eap_session, VALUE_PAIR *vps)
+static int mod_instantiate(void *instance, UNUSED CONF_SECTION *conf)
{
- REQUEST *request = eap_session->request;
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
-
- uint8_t sres_cat[SIM_VECTOR_GSM_SRES_SIZE * 3];
- uint8_t *p = sres_cat;
-
- uint8_t calc_mac[SIM_MAC_DIGEST_SIZE];
- ssize_t slen;
- VALUE_PAIR *mac;
-
- memcpy(p, eap_sim_session->keys.gsm.vector[0].sres, SIM_VECTOR_GSM_SRES_SIZE);
- p += SIM_VECTOR_GSM_SRES_SIZE;
- memcpy(p, eap_sim_session->keys.gsm.vector[1].sres, SIM_VECTOR_GSM_SRES_SIZE);
- p += SIM_VECTOR_GSM_SRES_SIZE;
- memcpy(p, eap_sim_session->keys.gsm.vector[2].sres, SIM_VECTOR_GSM_SRES_SIZE);
-
- mac = fr_pair_find_by_da(vps, attr_eap_sim_mac, TAG_ANY);
- if (!mac) {
- REDEBUG("Missing AT_MAC attribute");
- return -1;
- }
- if (mac->vp_length != SIM_MAC_DIGEST_SIZE) {
- REDEBUG("EAP-SIM-MAC has incorrect length, expected %u bytes got %zu bytes",
- SIM_MAC_DIGEST_SIZE, mac->vp_length);
- return -1;
- }
-
- slen = fr_sim_crypto_sign_packet(calc_mac, eap_session->this_round->response, true, EVP_sha1(),
- eap_sim_session->keys.k_aut, eap_sim_session->keys.k_aut_len,
- sres_cat, sizeof(sres_cat));
- if (slen < 0) {
- RPEDEBUG("Failed calculating MAC");
- return -1;
- } else if (slen == 0) {
- REDEBUG("Missing EAP-SIM-MAC attribute in packet buffer");
- return -1;
- }
-
- if (memcmp(mac->vp_octets, calc_mac, sizeof(calc_mac)) == 0) {
- RDEBUG2("EAP-SIM-MAC matches calculated MAC");
- } else {
- REDEBUG("EAP-SIM-MAC does not match calculated MAC");
- RHEXDUMP_INLINE2(mac->vp_octets, SIM_MAC_DIGEST_SIZE, "Received");
- RHEXDUMP_INLINE2(calc_mac, SIM_MAC_DIGEST_SIZE, "Expected");
- return -1;
- }
-
- eap_sim_session->challenge_success = true;
+ eap_aka_sim_common_conf_t *inst = talloc_get_type_abort(instance, eap_aka_sim_common_conf_t);
- /*
- * If the peer wants a Success notification, then
- * send a success notification, otherwise send a
- * normal EAP-Success.
- */
- if (fr_pair_find_by_da(vps, attr_eap_sim_result_ind, TAG_ANY)) {
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_SUCCESS_NOTIFICATION);
- return 1;
- }
+ if (mod_section_compile(&inst->actions, inst->virtual_server) < 0) return -1;
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_SUCCESS);
return 0;
}
-
-/** Authenticate a previously sent challenge
+/** Compile any virtual servers with the "eap-aka" namespace
*
*/
-static rlm_rcode_t mod_process(UNUSED void *instance, UNUSED void *thread, REQUEST *request)
-{
- eap_session_t *eap_session = eap_session_get(request);
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
-
- fr_sim_decode_ctx_t ctx = { .keys = &eap_sim_session->keys };
- VALUE_PAIR *subtype_vp, *from_peer, *vp;
- fr_cursor_t cursor;
-
- eap_sim_subtype_t subtype;
-
- int ret;
-
- /*
- * VPS is the data from the client
- */
- from_peer = eap_session->request->packet->vps;
-
- fr_cursor_init(&cursor, &request->packet->vps);
- fr_cursor_tail(&cursor);
-
- ret = fr_sim_decode(eap_session->request,
- &cursor,
- dict_eap_sim,
- eap_session->this_round->response->type.data,
- eap_session->this_round->response->type.length,
- &ctx);
- /*
- * RFC 4186 says we *MUST* notify, not just
- * send an EAP-Failure in this case where
- * we cannot decode an EAP-AKA packet.
- */
- if (ret < 0) {
- RPEDEBUG2("Failed decoding EAP-SIM attributes");
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
-
- vp = fr_cursor_current(&cursor);
- if (vp && RDEBUG_ENABLED2) {
- RDEBUG2("Decoded EAP-SIM attributes");
- log_request_pair_list(L_DBG_LVL_2, request, vp, NULL);
- }
-
- subtype_vp = fr_pair_find_by_da(from_peer, attr_eap_sim_subtype, TAG_ANY);
- if (!subtype_vp) {
- REDEBUG("Missing EAP-SIM-Subtype");
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
- subtype = subtype_vp->vp_uint16;
-
- switch (eap_sim_session->state) {
- /*
- * Response to our advertised versions and request for an ID
- * This is very similar to Identity negotiation in EAP-AKA[']
- */
- case EAP_SIM_SERVER_START:
- switch (subtype) {
- case EAP_SIM_START:
- if (process_eap_sim_start(eap_session, from_peer) == 0) return RLM_MODULE_HANDLED;
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
-
- /*
- * Case 1 where we're allowed to send an EAP-Failure
- *
- * This can happen in the case of a conservative
- * peer, where it refuses to provide the permanent
- * identity.
- */
- case EAP_SIM_CLIENT_ERROR:
- {
- char buff[20];
-
- vp = fr_pair_find_by_da(from_peer, attr_eap_sim_client_error_code, TAG_ANY);
- if (!vp) {
- REDEBUG("EAP-SIM Peer rejected SIM-Start (%s) with client-error message but "
- "has not supplied a client error code",
- fr_table_str_by_value(sim_id_request_table, eap_sim_session->id_req, "<INVALID>"));
- } else {
- REDEBUG("Client rejected SIM-Start (%s) with error: %s (%i)",
- fr_table_str_by_value(sim_id_request_table, eap_sim_session->id_req, "<INVALID>"),
- fr_pair_value_enum(vp, buff), vp->vp_uint16);
- }
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
- return RLM_MODULE_REJECT;
- }
-
- case EAP_SIM_NOTIFICATION:
- notification:
- {
- char buff[20];
-
- vp = fr_pair_afrom_da(from_peer, attr_eap_sim_notification);
- if (!vp) {
- REDEBUG2("Received SIM-Notification with no notification code");
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
-
- /*
- * Case 2 where we're allowed to send an EAP-Failure
- */
- if (!(vp->vp_uint16 & 0x8000)) {
- REDEBUG2("SIM-Notification %s (%i) indicates failure", fr_pair_value_enum(vp, buff),
- vp->vp_uint16);
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
- return RLM_MODULE_REJECT;
- }
-
- /*
- * ...if it's not a failure, then re-enter the
- * current state.
- */
- REDEBUG2("Got SIM-Notification %s (%i)", fr_pair_value_enum(vp, buff), vp->vp_uint16);
- eap_sim_state_enter(eap_session, eap_sim_session->state);
- return RLM_MODULE_HANDLED;
-
- default:
- unexpected_subtype:
- /*
- * RFC 4186 says we *MUST* notify, not just
- * send an EAP-Failure in this case.
- */
- REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
- }
-
- /*
- * Process the response to our previous challenge.
- */
- case EAP_SIM_SERVER_CHALLENGE:
- switch (subtype) {
- /*
- * A response to our EAP-Sim/Request/Challenge!
- */
- case EAP_SIM_CHALLENGE:
- switch (process_eap_sim_challenge(eap_session, from_peer)) {
- case 1:
- return RLM_MODULE_HANDLED;
-
- case 0:
- return RLM_MODULE_OK;
-
- case -1:
- default:
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
-
- case EAP_SIM_CLIENT_ERROR:
- {
- char buff[20];
-
- vp = fr_pair_find_by_da(from_peer, attr_eap_sim_client_error_code, TAG_ANY);
- if (!vp) {
- REDEBUG("EAP-SIM Peer rejected SIM-Challenge with client-error message but "
- "has not supplied a client error code");
- } else {
- REDEBUG("Client rejected SIM-Challenge with error: %s (%i)",
- fr_pair_value_enum(vp, buff), vp->vp_uint16);
- }
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
- return RLM_MODULE_REJECT;
- }
-
- case EAP_SIM_NOTIFICATION:
- goto notification;
-
- default:
- goto unexpected_subtype;
- }
-
- /*
- * Peer acked our failure
- */
- case EAP_SIM_SERVER_FAILURE_NOTIFICATION:
- switch (subtype) {
- case EAP_SIM_NOTIFICATION:
- RDEBUG2("SIM-Notification ACKed, sending EAP-Failure");
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
- return RLM_MODULE_REJECT;
-
- default:
- goto unexpected_subtype;
- }
-
- /*
- * Something bad happened...
- */
- default:
- rad_assert(0);
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
- return RLM_MODULE_HANDLED; /* We need to process more packets */
- }
-}
-
-/*
- * Initiate the EAP-SIM session by starting the state machine
- * and initiating the state.
- */
-static rlm_rcode_t mod_session_init(void *instance, UNUSED void *thread, REQUEST *request)
+static int mod_namespace_load(CONF_SECTION *server_cs)
{
- rlm_eap_sim_t *inst = talloc_get_type_abort(instance, rlm_eap_sim_t);
- eap_session_t *eap_session = eap_session_get(request);
- eap_sim_session_t *eap_sim_session;
-
- fr_sim_id_type_t type;
- fr_sim_method_hint_t method;
-
- MEM(eap_sim_session = talloc_zero(eap_session, eap_sim_session_t));
-
- eap_session->opaque = eap_sim_session;
-
- /*
- * Set default configuration, we may allow these
- * to be toggled by attributes later.
- */
- eap_sim_session->send_result_ind = inst->protected_success;
- eap_sim_session->id_req = SIM_ANY_ID_REQ; /* Set the default */
-
- /*
- * This value doesn't have be strong, but it is
- * good if it is different now and then.
- */
- eap_sim_session->sim_id = (fr_rand() & 0xff);
-
- /*
- * Save the keying material, because it could change on a subsequent retrieval.
- */
- RDEBUG2("New EAP-SIM session");
-
- /*
- * Process the identity that we received in the
- * EAP-Identity-Response and use it to determine
- * the initial request we send to the Supplicant.
- */
- if (fr_sim_id_type(&type, &method,
- eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) {
- RPWDEBUG2("Failed parsing identity, continuing anyway");
- }
-
- switch (method) {
- default:
- RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're attempting EAP-SIM",
- fr_table_str_by_value(sim_id_method_hint_table, method, "<INVALID>"));
- break;
-
- case SIM_METHOD_HINT_SIM:
- case SIM_METHOD_HINT_UNKNOWN:
- break;
- }
- eap_session->process = mod_process;
-
- /*
- * Figure out what type of identity we have
- * and use it to determine the initial
- * request we send.
- */
- switch (type) {
- /*
- * These types need to be transformed into something
- * usable before we can do anything.
- */
- case SIM_ID_TYPE_UNKNOWN:
- case SIM_ID_TYPE_PSEUDONYM:
- case SIM_ID_TYPE_FASTAUTH:
- /*
- * Permanent ID means we can just send the challenge
- */
- case SIM_ID_TYPE_PERMANENT:
- eap_sim_session->keys.identity_len = talloc_array_length(eap_session->identity) - 1;
- MEM(eap_sim_session->keys.identity = talloc_memdup(eap_sim_session, eap_session->identity,
- eap_sim_session->keys.identity_len));
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_START);
- return RLM_MODULE_HANDLED;
- }
-
- return RLM_MODULE_HANDLED;
+ return mod_section_compile(NULL, server_cs);
}
static int mod_load(void)
{
- if (fr_sim_init() < 0) return -1;
+ if (virtual_namespace_register("eap-sim", "eap-aka-sim", "eap/aka-sim", mod_namespace_load) < 0) return -1;
+
+ if (fr_aka_sim_init() < 0) return -1;
- sim_xlat_register();
+ fr_aka_sim_xlat_register();
return 0;
}
static void mod_unload(void)
{
- sim_xlat_unregister();
+ fr_aka_sim_xlat_unregister();
- fr_sim_free();
+ fr_aka_sim_free();
}
/*
.magic = RLM_MODULE_INIT,
.provides = { FR_EAP_METHOD_SIM },
- .inst_size = sizeof(rlm_eap_sim_t),
+
+ .inst_size = sizeof(eap_aka_sim_common_conf_t),
+ .inst_type = "eap_aka_sim_common_conf_t",
.config = submodule_config,
.onload = mod_load,
.unload = mod_unload,
- .session_init = mod_session_init, /* Initialise a new EAP session */
- .entry_point = mod_process, /* Process next round of EAP method */
+
+ .instantiate = mod_instantiate,
+
+ .session_init = aka_sim_state_machine_start, /* Initialise a new EAP session */
+ .namespace = &dict_eap_aka_sim
};
#include <freeradius-devel/util/dict.h>
-extern fr_dict_t *dict_eap_sim;
-extern fr_dict_t *dict_eap_aka;
+extern fr_dict_t *dict_eap_aka_sim;
-extern fr_dict_attr_t const *attr_eap_aka_autn;
-extern fr_dict_attr_t const *attr_eap_aka_ck;
-extern fr_dict_attr_t const *attr_eap_aka_ik;
-extern fr_dict_attr_t const *attr_eap_aka_rand;
-extern fr_dict_attr_t const *attr_eap_aka_xres;
+extern fr_dict_attr_t const *attr_eap_aka_sim_autn;
+extern fr_dict_attr_t const *attr_eap_aka_sim_ck;
+extern fr_dict_attr_t const *attr_eap_aka_sim_ik;
+extern fr_dict_attr_t const *attr_eap_aka_sim_rand;
+extern fr_dict_attr_t const *attr_eap_aka_sim_xres;
-extern fr_dict_attr_t const *attr_eap_sim_kc;
-extern fr_dict_attr_t const *attr_eap_sim_sres;
-extern fr_dict_attr_t const *attr_eap_sim_rand;
+extern fr_dict_attr_t const *attr_eap_aka_sim_kc;
+extern fr_dict_attr_t const *attr_eap_aka_sim_sres;
+extern fr_dict_attr_t const *attr_eap_aka_sim_rand;
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/rad_assert.h>
-#include <freeradius-devel/protocol/eap/aka/dictionary.h>
-#include <freeradius-devel/protocol/eap/sim/dictionary.h>
+#include <freeradius-devel/protocol/eap/aka-sim/dictionary.h>
+#include <freeradius-devel/protocol/eap/aka-sim/dictionary.h>
#include <freeradius-devel/unlang/base.h>
#include <freeradius-devel/server/module.h>
return 0;
}
-static void sigtran_client_signal(UNUSED REQUEST *request, UNUSED void *instance,
- UNUSED void *thread, void *rctx, fr_state_signal_t action)
+static void sigtran_client_signal(UNUSED void *instance, UNUSED void *thread, UNUSED REQUEST *request,
+ void *rctx, fr_state_signal_t action)
{
sigtran_transaction_t *txn = talloc_get_type_abort(rctx, sigtran_transaction_t);
txn->ctx.request = NULL; /* remove the link to the (now dead) request */
}
-static rlm_rcode_t sigtran_client_map_resume(REQUEST *request, UNUSED void *instance, UNUSED void *thread, void *rctx)
+static rlm_rcode_t sigtran_client_map_resume(UNUSED void *instance, UNUSED void *thread, REQUEST *request, void *rctx)
{
sigtran_transaction_t *txn = talloc_get_type_abort(rctx, sigtran_transaction_t);
rlm_rcode_t rcode;
RDEBUG2("SIM auth vector %i", i);
RINDENT();
- vp = fr_pair_afrom_da(request, attr_eap_sim_rand);
- fr_pair_value_memsteal(vp, vec->sim.rand, false);
+ vp = fr_pair_afrom_da(request, attr_eap_aka_sim_rand);
+ fr_pair_value_memsteal(vp, vec->sim.rand, true);
RDEBUG2("&control:%pP", vp);
fr_cursor_append(&cursor, vp);
- vp = fr_pair_afrom_da(request, attr_eap_sim_sres);
- fr_pair_value_memsteal(vp, vec->sim.sres, false);
+ vp = fr_pair_afrom_da(request, attr_eap_aka_sim_sres);
+ fr_pair_value_memsteal(vp, vec->sim.sres, true);
RDEBUG2("&control:%pP", vp);
fr_cursor_append(&cursor, vp);
- vp = fr_pair_afrom_da(request, attr_eap_sim_kc);
- fr_pair_value_memsteal(vp, vec->sim.kc, false);
+ vp = fr_pair_afrom_da(request, attr_eap_aka_sim_kc);
+ fr_pair_value_memsteal(vp, vec->sim.kc, true);
RDEBUG2("&control:%pP", vp);
fr_cursor_append(&cursor, vp);
REXDENT();
RDEBUG2("UMTS auth vector %i", i);
RINDENT();
- vp = fr_pair_afrom_da(request, attr_eap_aka_rand);
- fr_pair_value_memsteal(vp, vec->umts.rand, false);
+ vp = fr_pair_afrom_da(request, attr_eap_aka_sim_rand);
+ fr_pair_value_memsteal(vp, vec->umts.rand, true);
RDEBUG2("&control:%pP", vp);
fr_cursor_append(&cursor, vp);
- vp = fr_pair_afrom_da(request, attr_eap_aka_xres);
- fr_pair_value_memsteal(vp, vec->umts.xres, false);
+ vp = fr_pair_afrom_da(request, attr_eap_aka_sim_xres);
+ fr_pair_value_memsteal(vp, vec->umts.xres, true);
RDEBUG2("&control:%pP", vp);
fr_cursor_append(&cursor, vp);
- vp = fr_pair_afrom_da(request, attr_eap_aka_ck);
- fr_pair_value_memsteal(vp, vec->umts.ck, false);
+ vp = fr_pair_afrom_da(request, attr_eap_aka_sim_ck);
+ fr_pair_value_memsteal(vp, vec->umts.ck, true);
RDEBUG2("&control:%pP", vp);
fr_cursor_append(&cursor, vp);
- vp = fr_pair_afrom_da(request, attr_eap_aka_ik);
- fr_pair_value_memsteal(vp, vec->umts.ik, false);
+ vp = fr_pair_afrom_da(request, attr_eap_aka_sim_ik);
+ fr_pair_value_memsteal(vp, vec->umts.ik, true);
RDEBUG2("&control:%pP", vp);
fr_cursor_append(&cursor, vp);
- vp = fr_pair_afrom_da(request, attr_eap_aka_autn);
- fr_pair_value_memsteal(vp, vec->umts.authn, false);
+ vp = fr_pair_afrom_da(request, attr_eap_aka_sim_autn);
+ fr_pair_value_memsteal(vp, vec->umts.authn, true);
RDEBUG2("&control:%pP", vp);
fr_cursor_append(&cursor, vp);
REXDENT();
break;
default:
- ERROR("%i is not a valid version", req->version);
+ REDEBUG("%i is not a valid version", req->version);
goto error;
}
txn->ctx.request = request;
if (tmpl_aexpand(req, &imsi, request, inst->imsi, NULL, NULL) < 0) {
- ERROR("Failed retrieving IMSI");
+ REDEBUG("Failed retrieving IMSI");
goto error;
}
len = talloc_array_length(imsi) - 1;
if ((len != 16) && (len != 15)) {
- ERROR("IMSI must be 15 or 16 digits got %zu digits", len);
+ REDEBUG("IMSI must be 15 or 16 digits got %zu digits", len);
goto error;
}
if (sigtran_ascii_to_tbcd(req, &req->imsi, imsi) < 0) {
- ERROR("Failed converting ASCII to BCD");
+ REDEBUG("Failed converting ASCII to BCD");
goto error;
}
+ if (RDEBUG_ENABLED2) {
+ RDEBUG2("Sending MAPv%u request with IMSI \"%pV\"", req->version, fr_box_strvalue_buffer(imsi));
+ } else if (RDEBUG_ENABLED3){
+ RDEBUG3("Sending MAPv%u request with IMSI \"%pV\" (TBCD %pV)",
+ req->version, fr_box_strvalue_buffer(imsi), fr_box_octets_buffer(req->imsi));
+ }
+
/*
* FIXME - We shouldn't assume the pipe is always writable
*/
if (write(fd, &txn, sizeof(txn)) < 0) {
- ERROR("worker - ctrl_pipe (%i) write failed: %s", fd, fr_syserror(errno));
+ REDEBUG("worker - ctrl_pipe (%i) write failed: %s", fd, fr_syserror(errno));
goto error;
}
CONF_PARSER_TERMINATOR
};
-fr_dict_t *dict_eap_sim;
-fr_dict_t *dict_eap_aka;
+fr_dict_t *dict_eap_aka_sim;
-fr_dict_attr_t const *attr_eap_aka_autn;
-fr_dict_attr_t const *attr_eap_aka_ck;
-fr_dict_attr_t const *attr_eap_aka_ik;
-fr_dict_attr_t const *attr_eap_aka_rand;
-fr_dict_attr_t const *attr_eap_aka_xres;
+/*
+ * UMTS vector
+ */
+fr_dict_attr_t const *attr_eap_aka_sim_autn;
+fr_dict_attr_t const *attr_eap_aka_sim_ck;
+fr_dict_attr_t const *attr_eap_aka_sim_ik;
+fr_dict_attr_t const *attr_eap_aka_sim_xres;
-fr_dict_attr_t const *attr_eap_sim_kc;
-fr_dict_attr_t const *attr_eap_sim_sres;
-fr_dict_attr_t const *attr_eap_sim_rand;
+/*
+ * GSM vector
+ */
+fr_dict_attr_t const *attr_eap_aka_sim_kc;
+fr_dict_attr_t const *attr_eap_aka_sim_sres;
+
+/*
+ * Shared
+ */
+fr_dict_attr_t const *attr_eap_aka_sim_rand;
extern fr_dict_autoload_t rlm_sigtran_dict[];
fr_dict_autoload_t rlm_sigtran_dict[] = {
- { .out = &dict_eap_sim, .proto = "eap-sim" },
- { .out = &dict_eap_aka, .proto = "eap-aka" },
+ { .out = &dict_eap_aka_sim, .base_dir = "eap/aka-sim", .proto = "eap-aka-sim" },
{ NULL }
};
extern fr_dict_attr_autoload_t rlm_sigtran_dict_attr[];
fr_dict_attr_autoload_t rlm_sigtran_dict_attr[] = {
- { .out = &attr_eap_aka_autn, .name = "EAP-AKA-AUTN", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_ck, .name = "EAP-AKA-CK", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_ik, .name = "EAP-AKA-IK", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_rand, .name = "EAP-AKA-RAND", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
- { .out = &attr_eap_aka_xres, .name = "EAP-AKA-XRES", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka },
-
- { .out = &attr_eap_sim_kc, .name = "EAP-SIM-KC", .type = FR_TYPE_OCTETS, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_rand, .name = "EAP-SIM-RAND", .type = FR_TYPE_OCTETS, .dict = &dict_eap_sim },
- { .out = &attr_eap_sim_sres, .name = "EAP-SIM-SRES", .type = FR_TYPE_OCTETS, .dict = &dict_eap_sim },
+ { .out = &attr_eap_aka_sim_autn, .name = "AUTN", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_ck, .name = "CK", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_ik, .name = "IK", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_kc, .name = "KC", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_rand, .name = "RAND", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_sres, .name = "SRES", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
+ { .out = &attr_eap_aka_sim_xres, .name = "XRES", .type = FR_TYPE_OCTETS, .dict = &dict_eap_aka_sim },
{ NULL }
};
aka {
network_name = "testing123"
+ virtual_server = "eap-aka"
}
--- /dev/null
+type = aka_prime
+
+aka_prime {
+ network_name = "testing123"
+ virtual_server = "eap-aka"
+}
type = sim
sim {
+ virtual_server = "eap-sim"
}
$INCLUDE ${maindir}/mods-available/pap
$INCLUDE ${maindir}/mods-available/mschap
+ cache eap_aka_sim_cache {
+ #
+ # key:: Cache key.
+ #
+ key = "%{Session-ID}"
+
+ #
+ # ttl:: TTL for cache entries.
+ #
+ ttl = 15
+
+ #
+ # update <section> { ... }::
+ #
+ update {
+ &session-state:Session-Data := &session-state:Session-Data
+ &session-state:Counter := &session-state:Counter
+ }
+ }
+
eap {
default_eap_type = md5
ignore_unknown_eap_types = no
}
$INCLUDE ${testdir}/methods-enabled/
}
+
}
policy {
&EAP-TLS-Require-Client-Cert := yes
}
}
-
- # Test credentials for EAP-SIM, EAP-AKA, EAP-AKA'
- update control {
- &Sim-Ki := 0x465b5ce8b199b49faa5f0a2ee238a6bc
- &Sim-Opc := 0xcd63cb71954a9f4e48a5994e37a02baf
- &Sim-SQN := 3
- }
files
eap
}
}
recv Access-Request {
+ update request {
+ &User-Name := &outer.User-Name
+ }
files
pap
chap
send Access-Challenge {
}
}
+
+server eap-aka-prime {
+ namespace = eap-aka-prime
+
+ recv Identity-Response {
+ "%{debug_attr:&session-state:}"
+ if (!&session-state:Tmp-String-0) {
+ update reply {
+ &Any-ID-Req := yes
+ }
+ update session-state {
+ &Tmp-String-0 := yes
+ }
+ }
+ ok
+ }
+
+ send Identity-Request {
+ ok
+ }
+
+ send Challenge-Request {
+ update control {
+ &Sim-Ki := 0x465b5ce8b199b49faa5f0a2ee238a6bc
+ &Sim-Opc := 0xcd63cb71954a9f4e48a5994e37a02baf
+ &Sim-SQN := 3
+ }
+ update reply {
+ &Next-Reauth-Id := ""
+ &Next-Pseudonym := ""
+ }
+ }
+
+ store session {
+ eap_aka_sim_cache
+ }
+
+ load session {
+ eap_aka_sim_cache
+ }
+
+ clear session {
+ eap_aka_sim_cache
+ }
+}
+
+server eap-aka {
+ namespace = eap-aka
+
+ recv Identity-Response {
+ "%{debug_attr:&session-state:}"
+ if (!&session-state:Tmp-String-0) {
+ update reply {
+ &Any-ID-Req := yes
+ }
+ update session-state {
+ &Tmp-String-0 := yes
+ }
+ }
+ ok
+ }
+
+ send Identity-Request {
+ ok
+ }
+
+ send Challenge-Request {
+ update control {
+ &Sim-Ki := 0x465b5ce8b199b49faa5f0a2ee238a6bc
+ &Sim-Opc := 0xcd63cb71954a9f4e48a5994e37a02baf
+ &Sim-SQN := 3
+ }
+ update reply {
+ &Next-Reauth-Id := ""
+ &Next-Pseudonym := ""
+ }
+ }
+
+ store session {
+ eap_aka_sim_cache
+ }
+
+ load session {
+ eap_aka_sim_cache
+ }
+
+ clear session {
+ eap_aka_sim_cache
+ }
+}
+
+server eap-sim {
+ namespace = eap-sim
+
+ recv Identity-Response {
+ "%{debug_attr:&session-state:}"
+ if (!&session-state:Tmp-String-0) {
+ update reply {
+ &Any-ID-Req := yes
+ }
+ update session-state {
+ &Tmp-String-0 := yes
+ }
+ }
+ ok
+ }
+
+ send Identity-Request {
+ ok
+ }
+
+ send Challenge-Request {
+ update control {
+ &Sim-Ki := 0x465b5ce8b199b49faa5f0a2ee238a6bc
+ &Sim-Opc := 0xcd63cb71954a9f4e48a5994e37a02baf
+ &Sim-SQN := 3
+ }
+ update reply {
+ &Next-Reauth-Id := ""
+ &Next-Pseudonym := ""
+ }
+ ok
+ }
+
+ send Reauthentication-Request {
+ update reply {
+ &Next-Reauth-Id := ""
+ &Next-Pseudonym := ""
+ }
+ ok
+ }
+
+ store session {
+ eap_aka_sim_cache
+ }
+
+ load session {
+ eap_aka_sim_cache
+ }
+
+ clear session {
+ eap_aka_sim_cache
+ }
+}
eap {
type = sim
sim {
-
+ virtual_server = default
}
}
-if ("%{sim_id_method:&User-Name}" != 'AKA') {
+if ("%{aka_sim_id_method:User-Name}" != 'AKA') {
test_fail
} else {
test_pass
}
-if ("%{sim_id_type:&User-Name}" != 'Permanent') {
+if ("%{aka_sim_id_type:User-Name}" != 'Permanent') {
test_fail
} else {
test_pass
}
#
-# 0.1 - Encrypt the permanent ID
+# Encrypt the permanent ID
#
update control {
&Tmp-String-0 := '1420032219455258'
#
# 0.2 - Can we get the EAP method from the encrypted blob correctly?
#
-if ("%{sim_id_method:&control:User-Name}" != 'AKA') {
+if ("%{aka_sim_id_method:control:User-Name}" != 'AKA') {
test_fail
} else {
test_pass
#
# 0.3 - Can we identify the encrypted blob correctly?
#
-if ("%{sim_id_type:&control:User-Name}" != 'Pseudonym') {
+if ("%{aka_sim_id_type:control:User-Name}" != 'Pseudonym') {
test_fail
} else {
test_pass
#
# 1.2 - Can we identify the encrypted blob correctly?
#
-if ("%{sim_id_type:&control:User-Name[1]}" != 'Pseudonym') {
+if ("%{aka_sim_id_type:&control:User-Name[1]}" != 'Pseudonym') {
test_fail
} else {
test_pass
&User-Name := '14200322194552581'
}
-if ("%{sim_id_method:User-Name}" != '') {
+if ("%{aka_sim_id_method:User-Name}" != '') {
test_fail
} else {
test_pass
&User-Name := ''
}
-if ("%{sim_id_method:User-Name}" != '') {
+if ("%{aka_sim_id_method:User-Name}" != '') {
test_fail
} else {
test_pass
-if ("%{sim_id_method:User-Name}" != 'SIM') {
+if ("%{aka_sim_id_method:User-Name}" != 'SIM') {
test_fail
} else {
test_pass
}
-if ("%{sim_id_type:User-Name}" != 'Permanent') {
+if ("%{aka_sim_id_type:User-Name}" != 'Permanent') {
test_fail
} else {
test_pass
#
# Can we get the EAP method from the encrypted blob correctly?
#
-if ("%{sim_id_method:control:User-Name}" != 'SIM') {
+if ("%{aka_sim_id_method:control:User-Name}" != 'SIM') {
test_fail
} else {
test_pass
#
# Can we identify the encrypted blob correctly?
#
-if ("%{sim_id_type:control:User-Name}" != 'Pseudonym') {
+if ("%{aka_sim_id_type:control:User-Name}" != 'Pseudonym') {
test_fail
} else {
test_pass
need-feature tls
# Load the EAP-SIM module
-load eap-sim
-load-dictionary eap-aka eap/aka
+load eap-aka-sim
+load-dictionary eap-aka-sim eap/aka-sim
#
# Encode some AKA attributes with special formats
#
-# EAP-AKA-Res is an octets type attribute but with a length field containing the length
+# Res is an octets type attribute but with a length field containing the length
# of the value in bits. It may be between 32 - 128 bits in length.
decode-pair.aka_tp_decode 03 02 00 20 01 02 03 04
-data EAP-AKA-RES = 0x01020304
+data RES = 0x01020304
decode-pair.aka_tp_decode 03 05 00 80 01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08
-data EAP-AKA-RES = 0x01020304050607080102030405060708
+data RES = 0x01020304050607080102030405060708
decode-pair.aka_tp_decode 03 05 00 78 01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08
-data EAP-AKA-RES = 0x010203040506070801020304050607
+data RES = 0x010203040506070801020304050607
decode-pair.aka_tp_decode 04 04 01 02 03 04 05 06 07 08 09 aa bb cc dd ee
-data EAP-AKA-AUTS = 0x010203040506070809aabbccddee
+data AUTS = 0x010203040506070809aabbccddee
decode-pair.aka_tp_decode 86 01 00 00
-data EAP-AKA-Checkcode = 0x
+data Checkcode = 0x
decode-pair.aka_tp_decode 86 06 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 a0 b0 c0 d0
-data EAP-AKA-Checkcode = 0x0102030405060708090a0b0c0d0e0f00a0b0c0d0
+data Checkcode = 0x0102030405060708090a0b0c0d0e0f00a0b0c0d0
decode-pair.aka_tp_decode 86 09 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 a0 b0 c0 d0 a0 b0 c0 d0 a0 b0 c0 d0 a0 b0 c0 d0
-data EAP-AKA-Checkcode = 0x0102030405060708090a0b0c0d0e0f00a0b0c0d0a0b0c0d0a0b0c0d0a0b0c0d0
+data Checkcode = 0x0102030405060708090a0b0c0d0e0f00a0b0c0d0a0b0c0d0a0b0c0d0a0b0c0d0
# Only run the tests if we have TLS support
need-feature tls
-# Load the EAP-SIM module
-load eap-sim
-load-dictionary eap-aka eap/aka
+# Load the EAP-AKA/SIM encoder/decoder
+load eap-aka-sim
+load-dictionary eap-aka-sim eap/aka-sim
#
# Encode some AKA attributes with special formats
#
-# EAP-AKA-Res is an octets type attribute but with a length field containing the length
+# Res is an octets type attribute but with a length field containing the length
# of the value in bits. It may be between 32 - 128 bits in length.
-encode-pair.aka_tp_encode EAP-AKA-RES = 0x01020304
+encode-pair.aka_tp_encode RES = 0x01020304
data 03 02 00 20 01 02 03 04
-encode-pair.aka_tp_encode EAP-AKA-RES = 0x01020304050607080102030405060708
+encode-pair.aka_tp_encode RES = 0x01020304050607080102030405060708
data 03 05 00 80 01 02 03 04 05 06 07 08 01 02 03 04 05 06 07 08
-encode-pair.aka_tp_encode EAP-AKA-AUTS = 0x010203040506070809aabbccddee
+encode-pair.aka_tp_encode AUTS = 0x010203040506070809aabbccddee
data 04 04 01 02 03 04 05 06 07 08 09 aa bb cc dd ee
-encode-pair.aka_tp_encode EAP-AKA-Checkcode = 0x
+encode-pair.aka_tp_encode Checkcode = 0x
data 86 01 00 00
-encode-pair.aka_tp_encode EAP-AKA-Checkcode = 0x0102030405060708090a0b0c0d0e0f00a0b0c0d0
+encode-pair.aka_tp_encode Checkcode = 0x0102030405060708090a0b0c0d0e0f00a0b0c0d0
data 86 06 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 a0 b0 c0 d0
-encode-pair.aka_tp_encode EAP-AKA-Checkcode = 0x0102030405060708090a0b0c0d0e0f00a0b0c0d0a0b0c0d0a0b0c0d0a0b0c0d0
+encode-pair.aka_tp_encode Checkcode = 0x0102030405060708090a0b0c0d0e0f00a0b0c0d0a0b0c0d0a0b0c0d0a0b0c0d0
data 86 09 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 a0 b0 c0 d0 a0 b0 c0 d0 a0 b0 c0 d0 a0 b0 c0 d0
# Only run the tests if we have TLS support
need-feature tls
-# Load the EAP-SIM module
-load eap-sim
-load-dictionary eap-aka eap/aka
+# Load the EAP-AKA/SIM encoder/decoder
+load eap-aka-sim
+load-dictionary eap-aka-sim eap/aka-sim
# AKA_RES with invalid length (too small)
decode-pair.aka_tp_decode 04 03 aa bb aa aa aa aa bb bb bb bb
-data sim_decode_pair_value: Failed parsing non-skippable attribute 'EAP-AKA-AUTS': sim_decode_pair_value: Attribute "EAP-AKA-AUTS" needs a value of exactly 14 bytes, but value was 10 bytes
+data sim_decode_pair_value: Failed parsing non-skippable attribute 'AUTS': sim_decode_pair_value: Attribute "AUTS" needs a value of exactly 14 bytes, but value was 10 bytes
# AKA_RES with invalid length (too big)
decode-pair.aka_tp_decode 04 05 aa bb aa aa aa aa bb bb bb bb cc cc cc cc dd dd dd dd
-data sim_decode_pair_value: Failed parsing non-skippable attribute 'EAP-AKA-AUTS': sim_decode_pair_value: Attribute "EAP-AKA-AUTS" needs a value of exactly 14 bytes, but value was 18 bytes
+data sim_decode_pair_value: Failed parsing non-skippable attribute 'AUTS': sim_decode_pair_value: Attribute "AUTS" needs a value of exactly 14 bytes, but value was 18 bytes
# AKA_RES with invalid number of bits (too small)
decode-pair.aka_tp_decode 03 02 00 18 aa bb cc dd
# AKA_RES with invalid number of bits (too big - exceed limit)
decode-pair.aka_tp_decode 03 05 00 7f aa bb cc dd aa bb cc dd aa bb cc dd aa bb cc dd
-data sim_decode_pair_value: RES Length (127) is not a multiple of 8
-
-# AKA Rand only contains a single attribute
-decode-pair.aka_tp_decode 01 09 00 00 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 78
-data sim_decode_pair_value: Failed parsing non-skippable attribute 'EAP-AKA-RAND': sim_decode_pair_value: Attribute "EAP-AKA-RAND" needs a value of exactly 16 bytes, but value was 34 bytes
+data sim_decode_pair_value: RES Length (127) is not a multiple of 8
\ No newline at end of file
# Only run the tests if we have TLS support
need-feature tls
-# Load the EAP-SIM module
-load eap-sim
-load-dictionary eap-sim eap/sim
+# Load the EAP-AKA/SIM encoder/decoder
+load eap-aka-sim
+load-dictionary eap-aka-sim eap/aka-sim
# Boolean attribute
decode-pair.sim_tp_decode 0d 01 00 00
-data EAP-SIM-Any-ID-Req = yes
+data Any-ID-Req = yes
# Short
decode-pair.sim_tp_decode 10 01 ff ff
-data EAP-SIM-Selected-Version = 65535
+data Selected-Version = 65535
# Octets
decode-pair.sim_tp_decode 01 05 00 00 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77
-data EAP-SIM-RAND = 0x00112233445566770011223344556677
+data RAND = 0x00112233445566770011223344556677
decode-pair.sim_tp_decode 01 09 00 00 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 78
-data EAP-SIM-RAND = 0x00112233445566770011223344556677, EAP-SIM-RAND = 0x00112233445566770011223344556678
+data RAND = 0x00112233445566770011223344556677, RAND = 0x00112233445566770011223344556678
# Aliased (found)
decode-pair.sim_tp_decode 0c 01 04 02
-data EAP-SIM-Notification = Temporarily-Denied
+data Notification = Temporarily-Denied
# Aliased (notfound)
decode-pair.sim_tp_decode 0c 01 ff ff
-data EAP-SIM-Notification = 65535
+data Notification = 65535
# No string padding
decode-pair.sim_tp_decode 0e 05 00 10 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 66 69
-data EAP-SIM-Identity = "testing123@simfi"
+data Identity = "testing123@simfi"
# Three bytes string padding
decode-pair.sim_tp_decode 0e 05 00 0d 74 65 73 74 69 6e 67 31 32 33 40 73 69 00 00 00
-data EAP-SIM-Identity = "testing123@si"
+data Identity = "testing123@si"
# Two bytes string padding
decode-pair.sim_tp_decode 0e 05 00 0e 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 00 00
-data EAP-SIM-Identity = "testing123@sim"
+data Identity = "testing123@sim"
# One byte string padding
decode-pair.sim_tp_decode 0e 05 00 0f 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 66 00
-data EAP-SIM-Identity = "testing123@simf"
+data Identity = "testing123@simf"
# Empty string
decode-pair.sim_tp_decode 0e 01 00 00
-data EAP-SIM-Identity = ""
+data Identity = ""
# Two attributes
decode-pair.sim_tp_decode 0e 05 00 10 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 66 69 0d 01 00 00
-data EAP-SIM-Identity = "testing123@simfi", EAP-SIM-Any-ID-Req = yes
+data Identity = "testing123@simfi", Any-ID-Req = yes
# Array (one element)
decode-pair.sim_tp_decode 0f 02 00 02 00 02 00 00
-data EAP-SIM-Version-List = 2
+data Version-List = 2
# Array (multiple elements, with padding)
decode-pair.sim_tp_decode 0f 03 00 06 00 02 00 03 00 04 00 00
-data EAP-SIM-Version-List = 2, EAP-SIM-Version-List = 3, EAP-SIM-Version-List = 4
+data Version-List = 2, Version-List = 3, Version-List = 4
# Array (multiple elements, with no padding)
decode-pair.sim_tp_decode 0f 03 00 08 00 02 ff ff 00 ff ff 00
-data EAP-SIM-Version-List = 2, EAP-SIM-Version-List = 65535, EAP-SIM-Version-List = 255, EAP-SIM-Version-List = 65280
+data Version-List = 2, Version-List = 65535, Version-List = 255, Version-List = 65280
# Zero length array
decode-pair.sim_tp_decode 0f 01 00 00
# Mixture of known and than skippable unknown
decode-pair.sim_tp_decode 82 05 00 00 47 bd f4 5e 3d c5 69 da e8 fa 73 2a 69 44 a8 78
-data EAP-SIM-Counter = 0, Attr-130.255 = 0x00000000000000000000
+data Counter = 0, Attr-130.255 = 0x00000000000000000000
decode-pair.sim_tp_decode 82 05 00 00 b2 dd 1d a3 e1 fc 91 3d 94 6b 3f a1 ba 5d 73 e2
-data Attr-130.255 = 0x00000000000000000000, EAP-SIM-Counter = 0
+data Attr-130.255 = 0x00000000000000000000, Counter = 0
# RFC4186 A.3. example
decode-pair.sim_tp_decode_rfc4186 0f 02 00 02 00 01 00 00
-data EAP-SIM-Version-List = 1
+data Version-List = 1
# RFC4186 A.4. example
decode-pair.sim_tp_decode_rfc4186 07 05 00 00 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10 10 01 00 01
-data EAP-SIM-Nonce-MT = 0x0123456789abcdeffedcba9876543210, EAP-SIM-Selected-Version = 1
+data Nonce-MT = 0x0123456789abcdeffedcba9876543210, Selected-Version = 1
# RFC4186 A.5. example
decode-pair.sim_tp_decode_rfc4186 01 0d 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 81 05 00 00 9e 18 b0 c2 9a 65 22 63 c0 6e fb 54 dd 00 a8 95 82 2d 00 00 55 f2 93 9b bd b1 b1 9e a1 b4 7f c0 b3 e0 be 4c ab 2c f7 37 2d 98 e3 02 3c 6b b9 24 15 72 3d 58 ba d6 6c e0 84 e1 01 b6 0f 53 58 35 4b d4 21 82 78 ae a7 bf 2c ba ce 33 10 6a ed dc 62 5b 0c 1d 5a a6 7a 41 73 9a e5 b5 79 50 97 3f c7 ff 83 01 07 3c 6f 95 31 50 fc 30 3e a1 52 d1 e1 0a 2d 1f 4f 52 26 da a1 ee 90 05 47 22 52 bd b3 b7 1d 6f 0c 3a 34 90 31 6c 46 92 98 71 bd 45 cd fd bc a6 11 2f 07 f8 be 71 79 90 d2 5f 6d d7 f2 b7 b3 20 bf 4d 5a 99 2e 88 03 31 d7 29 94 5a ec 75 ae 5d 43 c8 ed a5 fe 62 33 fc ac 49 4e e6 7a 0d 50 4d 0b 05 00 00 fe f3 24 ac 39 62 b5 9f 3b d7 82 53 ae 4d cb 6a
-data EAP-SIM-RAND = 0x101112131415161718191a1b1c1d1e1f, EAP-SIM-RAND = 0x202122232425262728292a2b2c2d2e2f, EAP-SIM-RAND = 0x303132333435363738393a3b3c3d3e3f, EAP-SIM-IV = 0x9e18b0c29a652263c06efb54dd00a895, EAP-SIM-Next-Pseudonym = "w8w49PexCazWJ&xCIARmxuMKht5S1sxRDqXSEFBEg3DcZP9cIxTe5J4OyIwNGVzxeJOU1G", EAP-SIM-Next-Reauth-ID = "Y24fNSrz8BP274jOJaF17WfxI8YO7QX00pMXk9XMMVOw7broaNhTczuFq53aEpOkk3L0dm@eapsim.foo", EAP-SIM-MAC = 0xfef324ac3962b59f3bd78253ae4dcb6a
+data RAND = 0x101112131415161718191a1b1c1d1e1f, RAND = 0x202122232425262728292a2b2c2d2e2f, RAND = 0x303132333435363738393a3b3c3d3e3f, IV = 0x9e18b0c29a652263c06efb54dd00a895, Next-Pseudonym = "w8w49PexCazWJ&xCIARmxuMKht5S1sxRDqXSEFBEg3DcZP9cIxTe5J4OyIwNGVzxeJOU1G", Next-Reauth-ID = "Y24fNSrz8BP274jOJaF17WfxI8YO7QX00pMXk9XMMVOw7broaNhTczuFq53aEpOkk3L0dm@eapsim.foo", MAC = 0xfef324ac3962b59f3bd78253ae4dcb6a
# RFC4186 A.6. example
decode-pair.sim_tp_decode_rfc4186 0b 05 00 00 f5 6d 64 33 e6 8e d2 97 6a c1 19 37 fc 3d 11 54
-data EAP-SIM-MAC = 0xf56d6433e68ed2976ac11937fc3d1154
+data MAC = 0xf56d6433e68ed2976ac11937fc3d1154
# RFC4186 A.9. example
decode-pair.sim_tp_decode_rfc4186 81 05 00 00 d5 85 ac 77 86 b9 03 36 65 7c 77 b4 65 75 b9 c4 82 1d 00 00 68 62 91 a9 d2 ab c5 8c aa 32 94 b6 e8 5b 44 84 6c 44 e5 dc b2 de 8b 9e 80 d6 9d 49 85 8a 5d b8 4c dc 1c 9b c9 5c 01 b9 6b 6e ca 31 34 74 ae a6 d3 14 16 e1 9d aa 9d f7 0f 05 00 88 41 ca 80 14 96 4d 3b 30 a4 9b cf 43 e4 d3 f1 8e 86 29 5a 4a 2b 38 d9 6c 97 05 c2 bb b0 5c 4a ac e9 7d 5e af f5 64 04 6c 8b d3 0b c3 9b e5 e1 7a ce 2b 10 a6 0b 05 00 00 48 3a 17 99 b8 3d 7c d3 d0 a1 e4 01 d9 ee 47 70
-data EAP-SIM-IV = 0xd585ac7786b90336657c77b46575b9c4, EAP-SIM-Counter = 1, EAP-SIM-Nonce-S = 0x0123456789abcdeffedcba9876543210, EAP-SIM-Next-Reauth-ID = "uta0M0iyIsMwWp5TTdSdnOLvg2XDVf21OYt1vnfiMcs5dnIDHOIFVavIRzMRyzW6vFzdHW@eapsim.foo", EAP-SIM-MAC = 0x483a1799b83d7cd3d0a1e401d9ee4770
+data IV = 0xd585ac7786b90336657c77b46575b9c4, Counter = 1, Nonce-S = 0x0123456789abcdeffedcba9876543210, Next-Reauth-ID = "uta0M0iyIsMwWp5TTdSdnOLvg2XDVf21OYt1vnfiMcs5dnIDHOIFVavIRzMRyzW6vFzdHW@eapsim.foo", MAC = 0x483a1799b83d7cd3d0a1e401d9ee4770
# RFC4186 A.10. example
decode-pair.sim_tp_decode_rfc4186 81 05 00 00 cd f7 ff a6 5d e0 4c 02 6b 56 c8 6b 76 b1 02 ea 82 05 00 00 b6 ed d3 82 79 e2 a1 42 3c 1a fc 5c 45 5c 7d 56 0b 05 00 00 fa f7 6b 71 fb e2 d2 55 b9 6a 35 66 c9 15 c6 17
-data EAP-SIM-IV = 0xcdf7ffa65de04c026b56c86b76b102ea, EAP-SIM-Counter = 1, EAP-SIM-MAC = 0xfaf76b71fbe2d255b96a3566c915c617
+data IV = 0xcdf7ffa65de04c026b56c86b76b102ea, Counter = 1, MAC = 0xfaf76b71fbe2d255b96a3566c915c617
# RFC4186 A.10. example - With IV at the end of the packet
decode-pair.sim_tp_decode_rfc4186 82 05 00 00 b6 ed d3 82 79 e2 a1 42 3c 1a fc 5c 45 5c 7d 56 0b 05 00 00 fa f7 6b 71 fb e2 d2 55 b9 6a 35 66 c9 15 c6 17 81 05 00 00 cd f7 ff a6 5d e0 4c 02 6b 56 c8 6b 76 b1 02 ea
-data EAP-SIM-Counter = 1, EAP-SIM-MAC = 0xfaf76b71fbe2d255b96a3566c915c617, EAP-SIM-IV = 0xcdf7ffa65de04c026b56c86b76b102ea
+data Counter = 1, MAC = 0xfaf76b71fbe2d255b96a3566c915c617, IV = 0xcdf7ffa65de04c026b56c86b76b102ea
# Only run the tests if we have TLS support
need-feature tls
-# Load the EAP-SIM module
-load eap-sim
-load-dictionary eap-sim eap/sim
+# Load the EAP-AKA/SIM encoder/decoder
+load eap-aka-sim
+load-dictionary eap-aka-sim eap/aka-sim
# Boolean attribute
-encode-pair.sim_tp_encode EAP-SIM-Any-ID-Req = yes
+encode-pair.sim_tp_encode Any-ID-Req = yes
data 0d 01 00 00
# False Boolean attribute (should produce no data)
-encode-pair.sim_tp_encode EAP-SIM-Any-ID-Req = no
+encode-pair.sim_tp_encode Any-ID-Req = no
data
# Short
-encode-pair.sim_tp_encode EAP-SIM-Selected-Version = 1
+encode-pair.sim_tp_encode Selected-Version = 1
data 10 01 00 01
# Octets
-encode-pair.sim_tp_encode EAP-SIM-RAND = 0x00112233445566770011223344556677
+encode-pair.sim_tp_encode RAND = 0x00112233445566770011223344556677
data 01 05 00 00 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77
-encode-pair.sim_tp_encode EAP-SIM-RAND = 0x00112233445566770011223344556677, EAP-SIM-RAND = 0x00112233445566770011223344556678
+encode-pair.sim_tp_encode RAND = 0x00112233445566770011223344556677, RAND = 0x00112233445566770011223344556678
data 01 09 00 00 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 77 00 11 22 33 44 55 66 78
# Octets (autopad)
-encode-pair.sim_tp_encode EAP-SIM-RAND = 0x11
+encode-pair.sim_tp_encode RAND = 0x11
data 01 05 00 00 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# Empty octets
-encode-pair.sim_tp_encode EAP-SIM-RAND = 0x
+encode-pair.sim_tp_encode RAND = 0x
data 01 01 00 00
# Aliased
-encode-pair.sim_tp_encode EAP-SIM-Notification = Temporarily-Denied
+encode-pair.sim_tp_encode Notification = Temporarily-Denied
data 0c 01 04 02
# No string padding
-encode-pair.sim_tp_encode EAP-SIM-Identity = "testing123@simfi"
+encode-pair.sim_tp_encode Identity = "testing123@simfi"
data 0e 05 00 10 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 66 69
# Three bytes string padding
-encode-pair.sim_tp_encode EAP-SIM-Identity = "testing123@si"
+encode-pair.sim_tp_encode Identity = "testing123@si"
data 0e 05 00 0d 74 65 73 74 69 6e 67 31 32 33 40 73 69 00 00 00
# Two bytes string padding
-encode-pair.sim_tp_encode EAP-SIM-Identity = "testing123@sim"
+encode-pair.sim_tp_encode Identity = "testing123@sim"
data 0e 05 00 0e 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 00 00
# One byte string padding
-encode-pair.sim_tp_encode EAP-SIM-Identity = "testing123@simf"
+encode-pair.sim_tp_encode Identity = "testing123@simf"
data 0e 05 00 0f 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 66 00
# Empty string
-encode-pair.sim_tp_encode EAP-SIM-Identity = ""
+encode-pair.sim_tp_encode Identity = ""
data 0e 01 00 00
# Two attributes
-encode-pair.sim_tp_encode EAP-SIM-Identity = "testing123@simfi", EAP-SIM-Any-ID-Req = yes
+encode-pair.sim_tp_encode Identity = "testing123@simfi", Any-ID-Req = yes
data 0e 05 00 10 74 65 73 74 69 6e 67 31 32 33 40 73 69 6d 66 69 0d 01 00 00
# A single encrypted attribute
-encode-pair.sim_tp_encode EAP-SIM-Next-Pseudonym = "testing123"
+encode-pair.sim_tp_encode Next-Pseudonym = "testing123"
data 82 05 00 00 3f b8 34 1f f8 26 e0 4d 4a f3 f9 61 3c a9 84 26
decode-pair.sim_tp_decode -
-data EAP-SIM-Next-Pseudonym = "testing123"
+data Next-Pseudonym = "testing123"
# Encrypt attribute requiring padding
-encode-pair.sim_tp_encode EAP-SIM-Counter-Too-Small = yes
+encode-pair.sim_tp_encode Counter-Too-Small = yes
data 82 05 00 00 5a f8 99 3c 02 f5 6c 04 b8 6e bb 54 3a af 74 32
decode-pair.sim_tp_decode -
-data EAP-SIM-Counter-Too-Small = yes
+data Counter-Too-Small = yes
# Two encrypted attributes, one bool to extend plaintext so it's not a multiple block of block length
-encode-pair.sim_tp_encode EAP-SIM-Next-Pseudonym = "testing123", EAP-SIM-Counter-Too-Small = yes
+encode-pair.sim_tp_encode Next-Pseudonym = "testing123", Counter-Too-Small = yes
data 82 09 00 00 3f b8 34 1f f8 26 e0 4d 4a f3 f9 61 3c a9 84 26 0f 4a 53 ce 33 99 9e 4f 29 df a4 79 18 a9 57 dd
decode-pair.sim_tp_decode -
-data EAP-SIM-Next-Pseudonym = "testing123", EAP-SIM-Counter-Too-Small = yes
+data Next-Pseudonym = "testing123", Counter-Too-Small = yes
# Plaintext and encrypted attributes interleaved (this shouldn't happen usually, due to sorting)
-encode-pair.sim_tp_encode EAP-SIM-Next-Pseudonym = "testing123", EAP-SIM-Any-ID-Req = yes, EAP-SIM-Counter-Too-Small = yes
+encode-pair.sim_tp_encode Next-Pseudonym = "testing123", Any-ID-Req = yes, Counter-Too-Small = yes
data 82 05 00 00 3f b8 34 1f f8 26 e0 4d 4a f3 f9 61 3c a9 84 26 0d 01 00 00 82 05 00 00 5a f8 99 3c 02 f5 6c 04 b8 6e bb 54 3a af 74 32
decode-pair.sim_tp_decode -
-data EAP-SIM-Next-Pseudonym = "testing123", EAP-SIM-Any-ID-Req = yes, EAP-SIM-Counter-Too-Small = yes
+data Next-Pseudonym = "testing123", Any-ID-Req = yes, Counter-Too-Small = yes
# Array (one element)
-encode-pair.sim_tp_encode EAP-SIM-Version-List = 1
+encode-pair.sim_tp_encode Version-List = 1
data 0f 02 00 02 00 01 00 00
# Array (multiple elements, with padding)
-encode-pair.sim_tp_encode EAP-SIM-Version-List = 1, EAP-SIM-Version-List = 2, EAP-SIM-Version-List = 3
+encode-pair.sim_tp_encode Version-List = 1, Version-List = 2, Version-List = 3
data 0f 03 00 06 00 01 00 02 00 03 00 00
# Array (multiple elements, with no padding)
-encode-pair.sim_tp_encode EAP-SIM-Version-List = 1, EAP-SIM-Version-List = 2, EAP-SIM-Version-List = 3, EAP-SIM-Version-List = 4
+encode-pair.sim_tp_encode Version-List = 1, Version-List = 2, Version-List = 3, Version-List = 4
data 0f 03 00 08 00 01 00 02 00 03 00 04
# RFC4186 A.3. example
-encode-pair.sim_tp_encode_rfc4186 EAP-SIM-Version-List = 1
+encode-pair.sim_tp_encode_rfc4186 Version-List = 1
data 0f 02 00 02 00 01 00 00
# RFC4186 A.4. example
-encode-pair.sim_tp_encode_rfc4186 EAP-SIM-Nonce-MT = 0x0123456789abcdeffedcba9876543210, EAP-SIM-Selected-Version = 1
+encode-pair.sim_tp_encode_rfc4186 Nonce-MT = 0x0123456789abcdeffedcba9876543210, Selected-Version = 1
data 07 05 00 00 01 23 45 67 89 ab cd ef fe dc ba 98 76 54 32 10 10 01 00 01
# RFC4186 A.5. example
-encode-pair.sim_tp_encode_rfc4186 EAP-SIM-RAND = 0x101112131415161718191a1b1c1d1e1f, EAP-SIM-RAND = 0x202122232425262728292a2b2c2d2e2f, EAP-SIM-RAND = 0x303132333435363738393a3b3c3d3e3f, EAP-SIM-IV = 0x9e18b0c29a652263c06efb54dd00a895, EAP-SIM-Next-Pseudonym = "w8w49PexCazWJ&xCIARmxuMKht5S1sxRDqXSEFBEg3DcZP9cIxTe5J4OyIwNGVzxeJOU1G", EAP-SIM-Next-Reauth-ID = "Y24fNSrz8BP274jOJaF17WfxI8YO7QX00pMXk9XMMVOw7broaNhTczuFq53aEpOkk3L0dm@eapsim.foo"
+encode-pair.sim_tp_encode_rfc4186 RAND = 0x101112131415161718191a1b1c1d1e1f, RAND = 0x202122232425262728292a2b2c2d2e2f, RAND = 0x303132333435363738393a3b3c3d3e3f, IV = 0x9e18b0c29a652263c06efb54dd00a895, Next-Pseudonym = "w8w49PexCazWJ&xCIARmxuMKht5S1sxRDqXSEFBEg3DcZP9cIxTe5J4OyIwNGVzxeJOU1G", Next-Reauth-ID = "Y24fNSrz8BP274jOJaF17WfxI8YO7QX00pMXk9XMMVOw7broaNhTczuFq53aEpOkk3L0dm@eapsim.foo"
data 01 0d 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 81 05 00 00 9e 18 b0 c2 9a 65 22 63 c0 6e fb 54 dd 00 a8 95 82 2d 00 00 55 f2 93 9b bd b1 b1 9e a1 b4 7f c0 b3 e0 be 4c ab 2c f7 37 2d 98 e3 02 3c 6b b9 24 15 72 3d 58 ba d6 6c e0 84 e1 01 b6 0f 53 58 35 4b d4 21 82 78 ae a7 bf 2c ba ce 33 10 6a ed dc 62 5b 0c 1d 5a a6 7a 41 73 9a e5 b5 79 50 97 3f c7 ff 83 01 07 3c 6f 95 31 50 fc 30 3e a1 52 d1 e1 0a 2d 1f 4f 52 26 da a1 ee 90 05 47 22 52 bd b3 b7 1d 6f 0c 3a 34 90 31 6c 46 92 98 71 bd 45 cd fd bc a6 11 2f 07 f8 be 71 79 90 d2 5f 6d d7 f2 b7 b3 20 bf 4d 5a 99 2e 88 03 31 d7 29 94 5a ec 75 ae 5d 43 c8 ed a5 fe 62 33 fc ac 49 4e e6 7a 0d 50 4d
# RFC4186 A.9. example
-encode-pair.sim_tp_encode_rfc4186 EAP-SIM-IV = 0xd585ac7786b90336657c77b46575b9c4, EAP-SIM-Counter = 1, EAP-SIM-Nonce-S = 0x0123456789abcdeffedcba9876543210, EAP-SIM-Next-Reauth-ID = "uta0M0iyIsMwWp5TTdSdnOLvg2XDVf21OYt1vnfiMcs5dnIDHOIFVavIRzMRyzW6vFzdHW@eapsim.foo"
+encode-pair.sim_tp_encode_rfc4186 IV = 0xd585ac7786b90336657c77b46575b9c4, Counter = 1, Nonce-S = 0x0123456789abcdeffedcba9876543210, Next-Reauth-ID = "uta0M0iyIsMwWp5TTdSdnOLvg2XDVf21OYt1vnfiMcs5dnIDHOIFVavIRzMRyzW6vFzdHW@eapsim.foo"
data 81 05 00 00 d5 85 ac 77 86 b9 03 36 65 7c 77 b4 65 75 b9 c4 82 1d 00 00 68 62 91 a9 d2 ab c5 8c aa 32 94 b6 e8 5b 44 84 6c 44 e5 dc b2 de 8b 9e 80 d6 9d 49 85 8a 5d b8 4c dc 1c 9b c9 5c 01 b9 6b 6e ca 31 34 74 ae a6 d3 14 16 e1 9d aa 9d f7 0f 05 00 88 41 ca 80 14 96 4d 3b 30 a4 9b cf 43 e4 d3 f1 8e 86 29 5a 4a 2b 38 d9 6c 97 05 c2 bb b0 5c 4a ac e9 7d 5e af f5 64 04 6c 8b d3 0b c3 9b e5 e1 7a ce 2b 10 a6
# RFC4186 A.10. example
-encode-pair.sim_tp_encode_rfc4186 EAP-SIM-IV = 0xcdf7ffa65de04c026b56c86b76b102ea, EAP-SIM-Counter = 1
+encode-pair.sim_tp_encode_rfc4186 IV = 0xcdf7ffa65de04c026b56c86b76b102ea, Counter = 1
data 81 05 00 00 cd f7 ff a6 5d e0 4c 02 6b 56 c8 6b 76 b1 02 ea 82 05 00 00 b6 ed d3 82 79 e2 a1 42 3c 1a fc 5c 45 5c 7d 56
# Only run the tests if we have TLS support
need-feature tls
-# Load the EAP-SIM module
-load eap-sim
-load-dictionary eap-sim eap/sim
+# Load the EAP-AKA/SIM encoder/decoder
+load eap-aka-sim
+load-dictionary eap-aka-sim eap/aka-sim
# Zero length attribute
decode-pair.sim_tp_decode 0d 00