aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-eap.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-eap.c')
-rw-r--r--epan/dissectors/packet-eap.c1166
1 files changed, 1166 insertions, 0 deletions
diff --git a/epan/dissectors/packet-eap.c b/epan/dissectors/packet-eap.c
new file mode 100644
index 0000000000..d419598a1e
--- /dev/null
+++ b/epan/dissectors/packet-eap.c
@@ -0,0 +1,1166 @@
+/* packet-eap.c
+ * Routines for EAP Extensible Authentication Protocol dissection
+ * RFC 2284, RFC 3748
+ *
+ * $Id$
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+#include <epan/packet.h>
+#include <epan/conversation.h>
+#include "ppptypes.h"
+#include "reassemble.h"
+
+static int proto_eap = -1;
+static int hf_eap_code = -1;
+static int hf_eap_identifier = -1;
+static int hf_eap_len = -1;
+static int hf_eap_type = -1;
+static int hf_eap_type_nak = -1;
+
+static gint ett_eap = -1;
+
+static dissector_handle_t ssl_handle;
+
+#define EAP_REQUEST 1
+#define EAP_RESPONSE 2
+#define EAP_SUCCESS 3
+#define EAP_FAILURE 4
+
+static const value_string eap_code_vals[] = {
+ { EAP_REQUEST, "Request" },
+ { EAP_RESPONSE, "Response" },
+ { EAP_SUCCESS, "Success" },
+ { EAP_FAILURE, "Failure" },
+ { 0, NULL }
+};
+
+/*
+References:
+ 1) http://www.iana.org/assignments/ppp-numbers
+ PPP EAP REQUEST/RESPONSE TYPES
+ 2) http://www.ietf.org/internet-drafts/draft-ietf-pppext-rfc2284bis-02.txt
+ 3) RFC2284
+ 4) RFC3748
+ 5) http://www.iana.org/assignments/eap-numbers EAP registry
+*/
+
+#define EAP_TYPE_ID 1
+#define EAP_TYPE_NOTIFY 2
+#define EAP_TYPE_NAK 3
+#define EAP_TYPE_MD5 4
+#define EAP_TYPE_TLS 13
+#define EAP_TYPE_LEAP 17
+#define EAP_TYPE_SIM 18
+#define EAP_TYPE_TTLS 21
+#define EAP_TYPE_PEAP 25
+#define EAP_TYPE_MSCHAPV2 26
+
+static const value_string eap_type_vals[] = {
+ {EAP_TYPE_ID, "Identity [RFC3748]" },
+ {EAP_TYPE_NOTIFY,"Notification [RFC3748]" },
+ {EAP_TYPE_NAK, "Nak (Response only) [RFC3748]" },
+ {EAP_TYPE_MD5, "MD5-Challenge [RFC3748]" },
+ { 5, "One Time Password (OTP) [RFC2289]" },
+ { 6, "Generic Token Card [RFC3748]" },
+ { 7, "?? RESERVED ?? " }, /* ??? */
+ { 8, "?? RESERVED ?? " }, /* ??? */
+ { 9, "RSA Public Key Authentication [Whelan]" },
+ { 10, "DSS Unilateral [Nace]" },
+ { 11, "KEA [Nace]" },
+ { 12, "KEA-VALIDATE [Nace]" },
+ {EAP_TYPE_TLS, "EAP-TLS [RFC2716] [Aboba]" },
+ { 14, "Defender Token (AXENT) [Rosselli]" },
+ { 15, "RSA Security SecurID EAP [Asnes, Liberman]" },
+ { 16, "Arcot Systems EAP [Jerdonek]" },
+ {EAP_TYPE_LEAP,"EAP-Cisco Wireless (LEAP) [Norman]" },
+ {EAP_TYPE_SIM, "EAP-SIM Nokia IP smart card authentication [Haverinen]" },
+ { 19, "SRP-SHA1 Part 1 [Carlson]" },
+ { 20, "SRP-SHA1 Part 2 [Carlson]" },
+ {EAP_TYPE_TTLS,"EAP-TTLS [Funk]" },
+ { 22, "Remote Access Service [Fields]" },
+ { 23, "UMTS Authentication and Key Agreement [Haverinen]" },
+ { 24, "EAP-3Com Wireless [Young]" },
+ {EAP_TYPE_PEAP,"PEAP [Palekar]" },
+ {EAP_TYPE_MSCHAPV2,"MS-EAP-Authentication [Palekar]" },
+ { 27, "Mutual Authentication w/Key Exchange (MAKE)[Berrendonner]" },
+ { 28, "CRYPTOCard [Webb]" },
+ { 29, "EAP-MSCHAP-V2 [Potter]" },
+ { 30, "DynamID [Merlin]" },
+ { 31, "Rob EAP [Ullah]" },
+ { 32, "SecurID EAP [Josefsson]" },
+ { 33, "MS-Authentication-TLV [Palekar]" },
+ { 34, "SentriNET [Kelleher]" },
+ { 35, "EAP-Actiontec Wireless [Chang]" },
+ { 36, "Cogent Systems Biometrics Authentication EAP [Xiong]" },
+ { 37, "AirFortress EAP [Hibbard]" },
+ { 38, "EAP-HTTP Digest [Tavakoli]" },
+ { 39, "SecureSuite EAP [Clements]" },
+ { 40, "DeviceConnect EAP [Pitard]" },
+ { 41, "EAP-SPEKE [Zick]" },
+ { 42, "EAP-MOBAC [Rixom]" },
+ { 43, "EAP-FAST [Cam-Winget]" },
+ { 44, "ZoneLabs EAP (ZLXEAP) [Bogue]" },
+ { 45, "EAP-Link [Zick]" },
+ { 254, "RESERVED for the Expanded Type [RFC3748]" },
+ { 255, "EXPERIMENTAL [RFC3748]" },
+ { 0, NULL }
+
+};
+
+/*
+ * State information for EAP-TLS (RFC2716) and Lightweight EAP:
+ *
+ * http://www.missl.cs.umd.edu/wireless/ethereal/leap.txt
+ *
+ * Attach to all conversations:
+ *
+ * a sequence number to be handed to "fragment_add_seq()" as
+ * the fragment sequence number - if it's -1, no reassembly
+ * is in progress, but if it's not, it's the sequence number
+ * to use for the current fragment;
+ *
+ * a value to be handed to "fragment_add_seq()" as the
+ * reassembly ID - when a reassembly is started, it's set to
+ * the frame number of the current frame, i.e. the frame
+ * that starts the reassembly;
+ *
+ * an indication of the current state of LEAP negotiation,
+ * with -1 meaning no LEAP negotiation is in progress.
+ *
+ * Attach to frames containing fragments of EAP-TLS messages the
+ * reassembly ID for those fragments, so we can find the reassembled
+ * data after the first pass through the packets.
+ *
+ * Attach to LEAP frames the state of the LEAP negotiation when the
+ * frame was processed, so we can properly dissect
+ * the LEAP message after the first pass through the packets.
+ *
+ * Attach to all conversations both pieces of information, to keep
+ * track of EAP-TLS reassembly and the LEAP state machine.
+ */
+static GMemChunk *conv_state_chunk = NULL;
+
+typedef struct {
+ int eap_tls_seq;
+ guint32 eap_reass_cookie;
+ int leap_state;
+} conv_state_t;
+
+static GMemChunk *frame_state_chunk = NULL;
+
+typedef struct {
+ int info; /* interpretation depends on EAP message type */
+} frame_state_t;
+
+/*********************************************************************
+ EAP-TLS
+RFC2716
+**********************************************************************/
+
+/*
+from RFC2716, pg 17
+
+ Flags
+
+ 0 1 2 3 4 5 6 7 8
+ +-+-+-+-+-+-+-+-+
+ |L M S R R Vers |
+ +-+-+-+-+-+-+-+-+
+
+ L = Length included
+ M = More fragments
+ S = EAP-TLS start
+ R = Reserved
+ Vers = PEAP version (Reserved for TLS and TTLS)
+*/
+
+#define EAP_TLS_FLAG_L 0x80 /* Length included */
+#define EAP_TLS_FLAG_M 0x40 /* More fragments */
+#define EAP_TLS_FLAG_S 0x20 /* EAP-TLS start */
+#define EAP_PEAP_FLAG_VERSION 0x07 /* EAP-PEAP version */
+
+/*
+ * reassembly of EAP-TLS
+ */
+static GHashTable *eaptls_fragment_table = NULL;
+
+static int hf_eaptls_fragment = -1;
+static int hf_eaptls_fragments = -1;
+static int hf_eaptls_fragment_overlap = -1;
+static int hf_eaptls_fragment_overlap_conflict = -1;
+static int hf_eaptls_fragment_multiple_tails = -1;
+static int hf_eaptls_fragment_too_long_fragment = -1;
+static int hf_eaptls_fragment_error = -1;
+static gint ett_eaptls_fragment = -1;
+static gint ett_eaptls_fragments = -1;
+static gint ett_eap_sim_attr = -1;
+
+static const fragment_items eaptls_frag_items = {
+ &ett_eaptls_fragment,
+ &ett_eaptls_fragments,
+ &hf_eaptls_fragments,
+ &hf_eaptls_fragment,
+ &hf_eaptls_fragment_overlap,
+ &hf_eaptls_fragment_overlap_conflict,
+ &hf_eaptls_fragment_multiple_tails,
+ &hf_eaptls_fragment_too_long_fragment,
+ &hf_eaptls_fragment_error,
+ NULL,
+ "fragments"
+};
+
+/*********************************************************************
+**********************************************************************/
+
+static gboolean
+test_flag(unsigned char flag, unsigned char mask)
+{
+ return ( ( flag & mask ) != 0 );
+}
+
+static void
+eaptls_defragment_init(void)
+{
+ fragment_table_init(&eaptls_fragment_table);
+}
+
+static void
+eap_init_protocol(void)
+{
+ if (conv_state_chunk != NULL)
+ g_mem_chunk_destroy(conv_state_chunk);
+ if (frame_state_chunk != NULL)
+ g_mem_chunk_destroy(frame_state_chunk);
+
+ conv_state_chunk = g_mem_chunk_new("conv_state_chunk",
+ sizeof (conv_state_t),
+ 10 * sizeof (conv_state_t),
+ G_ALLOC_ONLY);
+
+ frame_state_chunk = g_mem_chunk_new("frame_state_chunk",
+ sizeof (frame_state_t),
+ 100 * sizeof (frame_state_t),
+ G_ALLOC_ONLY);
+}
+
+static void
+dissect_eap_mschapv2(proto_tree *eap_tree, tvbuff_t *tvb, int offset,
+ gint size)
+{
+ gint left = size;
+ gint ms_len;
+ guint8 value_size;
+ enum {
+ MS_CHAPv2_CHALLENGE = 1,
+ MS_CHAPv2_RESPONSE = 2,
+ MS_CHAPv2_SUCCESS = 3,
+ MS_CHAPv2_FAILURE = 4,
+ MS_CHAPv2_CHANGE_PASSWORD = 5
+ } opcode;
+ static const value_string opcodes[] = {
+ { MS_CHAPv2_CHALLENGE, "Challenge" },
+ { MS_CHAPv2_RESPONSE, "Response" },
+ { MS_CHAPv2_SUCCESS, "Success" },
+ { MS_CHAPv2_FAILURE, "Failure" },
+ { MS_CHAPv2_CHANGE_PASSWORD, "Change-Password" },
+ { 0, NULL }
+ };
+
+ /* OpCode (1 byte), MS-CHAPv2-ID (1 byte), MS-Length (2 bytes), Data */
+ opcode = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(eap_tree, tvb, offset, 1,
+ "OpCode: %d (%s)",
+ opcode, val_to_str(opcode, opcodes, "Unknown"));
+ offset++;
+ left--;
+ if (left <= 0)
+ return;
+
+ proto_tree_add_text(eap_tree, tvb, offset, 1, "MS-CHAPv2-ID: %d",
+ tvb_get_guint8(tvb, offset));
+ offset++;
+ left--;
+ if (left <= 0)
+ return;
+
+ ms_len = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_text(eap_tree, tvb, offset, 2, "MS-Length: %d%s",
+ ms_len,
+ ms_len != size ? " (invalid len)" : "");
+ offset += 2;
+ left -= 2;
+
+ switch (opcode) {
+ case MS_CHAPv2_CHALLENGE:
+ if (left <= 0)
+ break;
+ value_size = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(eap_tree, tvb, offset, 1,
+ "Value-Size: %d", value_size);
+ offset++;
+ left--;
+ proto_tree_add_text(eap_tree, tvb, offset, value_size,
+ "Challenge: %s",
+ tvb_bytes_to_str(tvb, offset, value_size));
+ offset += value_size;
+ left -= value_size;
+ if (left <= 0)
+ break;
+ proto_tree_add_text(eap_tree, tvb, offset, left,
+ "Name: %s",
+ tvb_format_text(tvb, offset, left));
+ break;
+ case MS_CHAPv2_RESPONSE:
+ if (left <= 0)
+ break;
+ value_size = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(eap_tree, tvb, offset, 1,
+ "Value-Size: %d", value_size);
+ offset++;
+ left--;
+ if (value_size == 49) {
+ proto_tree_add_text(eap_tree, tvb, offset, 16,
+ "Peer-Challenge: %s",
+ tvb_bytes_to_str(tvb, offset, 16));
+ offset += 16;
+ proto_tree_add_text(eap_tree, tvb, offset, 8,
+ "Reserved, must be zero: %s",
+ tvb_bytes_to_str(tvb, offset, 8));
+ offset += 8;
+ proto_tree_add_text(eap_tree, tvb, offset, 24,
+ "NT-Response: %s",
+ tvb_bytes_to_str(tvb, offset, 24));
+ offset += 24;
+ proto_tree_add_text(eap_tree, tvb, offset, 1,
+ "Flags: %d",
+ tvb_get_guint8(tvb, offset));
+ offset++;
+ left -= value_size;
+ } else {
+ proto_tree_add_text(eap_tree, tvb, offset, value_size,
+ "Response (unknown length): %s",
+ tvb_bytes_to_str(tvb, offset,
+ value_size));
+ offset += value_size;
+ left -= value_size;
+ }
+ if (left <= 0)
+ break;
+ proto_tree_add_text(eap_tree, tvb, offset, left,
+ "Name: %s",
+ tvb_format_text(tvb, offset, left));
+ break;
+ case MS_CHAPv2_SUCCESS:
+ if (left <= 0)
+ break;
+ proto_tree_add_text(eap_tree, tvb, offset, left,
+ "Message: %s",
+ tvb_format_text(tvb, offset, left));
+ break;
+ case MS_CHAPv2_FAILURE:
+ if (left <= 0)
+ break;
+ proto_tree_add_text(eap_tree, tvb, offset, left,
+ "Failure Request: %s",
+ tvb_format_text(tvb, offset, left));
+ break;
+ default:
+ proto_tree_add_text(eap_tree, tvb, offset, left,
+ "Data (%d byte%s) Value: %s",
+ left, plurality(left, "", "s"),
+ tvb_bytes_to_str(tvb, offset, left));
+ break;
+ }
+}
+
+static void
+dissect_eap_sim(proto_tree *eap_tree, tvbuff_t *tvb, int offset, gint size)
+{
+ gint left = size;
+ enum {
+ SIM_START = 10,
+ SIM_CHALLENGE = 11,
+ SIM_NOTIFICATION = 12,
+ SIM_RE_AUTHENTICATION = 13,
+ SIM_CLIENT_ERROR = 14
+ } subtype;
+ static const value_string subtypes[] = {
+ { SIM_START, "Start" },
+ { SIM_CHALLENGE, "Challenge" },
+ { SIM_NOTIFICATION, "Notification" },
+ { SIM_RE_AUTHENTICATION, "Re-authentication" },
+ { SIM_CLIENT_ERROR, "Client-Error" },
+ { 0, NULL }
+ };
+ static const value_string attributes[] = {
+ { 1, "AT_RAND" },
+ { 6, "AT_PADDING" },
+ { 7, "AT_NONCE_MT" },
+ { 10, "AT_PERMANENT_ID_REQ" },
+ { 11, "AT_MAC" },
+ { 12, "AT_NOTIFICATION" },
+ { 13, "AT_ANY_ID_REQ" },
+ { 14, "AT_IDENTITY" },
+ { 15, "AT_VERSION_LIST" },
+ { 16, "AT_SELECTED_VERSION" },
+ { 17, "AT_FULLAUTH_ID_REQ" },
+ { 18, "AT_COUNTER" },
+ { 19, "AT_COUNTER_TOO_SMALL" },
+ { 20, "AT_NONCE_S" },
+ { 21, "AT_CLIENT_ERROR_CODE" },
+ { 129, "AT_IV" },
+ { 130, "AT_ENCR_DATA" },
+ { 132, "AT_NEXT_PSEUDONYM" },
+ { 133, "AT_NEXT_REAUTH_ID" },
+ { 0, NULL }
+ };
+
+ subtype = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(eap_tree, tvb, offset, 1,
+ "subtype: %d (%s)",
+ subtype, val_to_str(subtype, subtypes, "Unknown"));
+
+ offset++;
+ left--;
+
+ if (left < 2)
+ return;
+ proto_tree_add_text(eap_tree, tvb, offset, 2, "Reserved: %d",
+ tvb_get_ntohs(tvb, offset));
+ offset += 2;
+ left -= 2;
+
+ /* Rest of EAP-SIM data is in Type-Len-Value format. */
+ while (left >= 2) {
+ guint8 type, length;
+ proto_item *pi;
+ proto_tree *attr_tree;
+ int aoffset;
+ gint aleft;
+ aoffset = offset;
+ type = tvb_get_guint8(tvb, aoffset);
+ length = tvb_get_guint8(tvb, aoffset + 1);
+ aleft = 4 * length;
+
+ pi = proto_tree_add_text(eap_tree, tvb, aoffset, aleft,
+ "Attribute: %s",
+ val_to_str(type, attributes,
+ "Unknown %u"));
+ attr_tree = proto_item_add_subtree(pi, ett_eap_sim_attr);
+ proto_tree_add_text(attr_tree, tvb, aoffset, 1,
+ "Type: %u", type);
+ aoffset++;
+ aleft--;
+
+ if (aleft <= 0)
+ break;
+ proto_tree_add_text(attr_tree, tvb, aoffset, 1,
+ "Length: %d (%d bytes)",
+ length, 4 * length);
+ aoffset++;
+ aleft--;
+ proto_tree_add_text(attr_tree, tvb, aoffset, aleft,
+ "Value: %s",
+ tvb_bytes_to_str(tvb, aoffset, aleft));
+
+ offset += 4 * length;
+ left -= 4 * length;
+ }
+}
+
+static int
+dissect_eap_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ gboolean fragmented)
+{
+ guint8 eap_code;
+ guint8 eap_id;
+ guint16 eap_len;
+ guint8 eap_type;
+ gint len;
+ conversation_t *conversation;
+ conv_state_t *conversation_state;
+ frame_state_t *packet_state;
+ int leap_state;
+ proto_tree *ti;
+ proto_tree *eap_tree = NULL;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "EAP");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ eap_code = tvb_get_guint8(tvb, 0);
+
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_add_str(pinfo->cinfo, COL_INFO,
+ val_to_str(eap_code, eap_code_vals, "Unknown code (0x%02X)"));
+
+ /*
+ * Find a conversation to which we belong; create one if we don't find
+ * it.
+ *
+ * We use the source and destination addresses, and the *matched* port
+ * number, because if this is running over RADIUS, there's no guarantee
+ * that the source port number for request and the destination port
+ * number for replies will be the same in all messages - the client
+ * may use different port numbers for each request.
+ *
+ * We have to pair up the matched port number with the corresponding
+ * address; we determine which that is by comparing it with the
+ * destination port - if it matches, we matched on the destination
+ * port (this is a request), otherwise we matched on the source port
+ * (this is a reply).
+ *
+ * XXX - what if we're running over a TCP or UDP protocol with a
+ * heuristic dissector, meaning the matched port number won't be set?
+ *
+ * XXX - what if we have a capture file with captures on multiple
+ * PPP interfaces, with LEAP traffic on all of them? How can we
+ * keep them separate? (Or is that not going to happen?)
+ */
+ if (pinfo->destport == pinfo->match_port) {
+ conversation = find_conversation(&pinfo->dst, &pinfo->src,
+ pinfo->ptype, pinfo->destport,
+ 0, NO_PORT_B);
+ } else {
+ conversation = find_conversation(&pinfo->src, &pinfo->dst,
+ pinfo->ptype, pinfo->srcport,
+ 0, NO_PORT_B);
+ }
+ if (conversation == NULL) {
+ if (pinfo->destport == pinfo->match_port) {
+ conversation = conversation_new(&pinfo->dst, &pinfo->src,
+ pinfo->ptype, pinfo->destport,
+ 0, NO_PORT2);
+ } else {
+ conversation = conversation_new(&pinfo->src, &pinfo->dst,
+ pinfo->ptype, pinfo->srcport,
+ 0, NO_PORT2);
+ }
+ }
+
+ /*
+ * Get the state information for the conversation; attach some if
+ * we don't find it.
+ */
+ conversation_state = conversation_get_proto_data(conversation, proto_eap);
+ if (conversation_state == NULL) {
+ /*
+ * Attach state information to the conversation.
+ */
+ conversation_state = g_mem_chunk_alloc(conv_state_chunk);
+ conversation_state->eap_tls_seq = -1;
+ conversation_state->eap_reass_cookie = 0;
+ conversation_state->leap_state = -1;
+ conversation_add_proto_data(conversation, proto_eap, conversation_state);
+ }
+
+ /*
+ * Set this now, so that it gets remembered even if we throw an exception
+ * later.
+ */
+ if (eap_code == EAP_FAILURE)
+ conversation_state->leap_state = -1;
+
+ eap_id = tvb_get_guint8(tvb, 1);
+
+ eap_len = tvb_get_ntohs(tvb, 2);
+ len = eap_len;
+
+ if (fragmented) {
+ /*
+ * This is an EAP fragment inside, for example, RADIUS. If we don't
+ * have all of the packet data, return the negative of the amount of
+ * additional data we need.
+ */
+ int reported_len = tvb_reported_length_remaining(tvb, 0);
+
+ if (reported_len < len)
+ return -(len - reported_len);
+ }
+
+ if (tree) {
+ ti = proto_tree_add_item(tree, proto_eap, tvb, 0, len, FALSE);
+ eap_tree = proto_item_add_subtree(ti, ett_eap);
+
+ proto_tree_add_uint(eap_tree, hf_eap_code, tvb, 0, 1, eap_code);
+ }
+
+ if (tree)
+ proto_tree_add_item(eap_tree, hf_eap_identifier, tvb, 1, 1, FALSE);
+
+ if (tree)
+ proto_tree_add_uint(eap_tree, hf_eap_len, tvb, 2, 2, eap_len);
+
+ switch (eap_code) {
+
+ case EAP_SUCCESS:
+ case EAP_FAILURE:
+ break;
+
+ case EAP_REQUEST:
+ case EAP_RESPONSE:
+ eap_type = tvb_get_guint8(tvb, 4);
+
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
+ val_to_str(eap_type, eap_type_vals,
+ "Unknown type (0x%02X)"));
+ if (tree)
+ proto_tree_add_uint(eap_tree, hf_eap_type, tvb, 4, 1, eap_type);
+
+ if (len > 5) {
+ int offset = 5;
+ gint size = len - offset;
+
+ switch (eap_type) {
+ /*********************************************************************
+ **********************************************************************/
+ case EAP_TYPE_ID:
+ if (tree) {
+ proto_tree_add_text(eap_tree, tvb, offset, size,
+ "Identity (%d byte%s): %s",
+ size, plurality(size, "", "s"),
+ tvb_format_text(tvb, offset, size));
+ }
+ if(!pinfo->fd->flags.visited)
+ conversation_state->leap_state = 0;
+ break;
+
+ /*********************************************************************
+ **********************************************************************/
+ case EAP_TYPE_NOTIFY:
+ if (tree) {
+ proto_tree_add_text(eap_tree, tvb, offset, size,
+ "Notification (%d byte%s): %s",
+ size, plurality(size, "", "s"),
+ tvb_format_text(tvb, offset, size));
+ }
+ break;
+ /*********************************************************************
+ **********************************************************************/
+ case EAP_TYPE_NAK:
+ if (tree) {
+ proto_tree_add_item(eap_tree, hf_eap_type_nak, tvb,
+ offset, 1, FALSE);
+ }
+ break;
+ /*********************************************************************
+ **********************************************************************/
+ case EAP_TYPE_MD5:
+ if (tree) {
+ guint8 value_size = tvb_get_guint8(tvb, offset);
+ gint extra_len = size - 1 - value_size;
+ proto_tree_add_text(eap_tree, tvb, offset, 1, "Value-Size: %d%s",
+ value_size,
+ value_size > size - 1 ? " (overflow)": "");
+ if (value_size > size - 1)
+ value_size = size - 1;
+ offset++;
+ proto_tree_add_text(eap_tree, tvb, offset, value_size,
+ "Value: %s",
+ tvb_bytes_to_str(tvb, offset, value_size));
+ offset += value_size;
+ if (extra_len > 0) {
+ proto_tree_add_text(eap_tree, tvb, offset, extra_len,
+ "Extra data (%d byte%s): %s", extra_len,
+ plurality(extra_len, "", "s"),
+ tvb_bytes_to_str(tvb, offset, extra_len));
+ }
+ }
+ break;
+ /*********************************************************************
+ EAP-TLS
+ **********************************************************************/
+ case EAP_TYPE_PEAP:
+ case EAP_TYPE_TTLS:
+ case EAP_TYPE_TLS:
+ {
+ guint8 flags = tvb_get_guint8(tvb, offset);
+ gboolean more_fragments;
+ gboolean has_length;
+ guint32 length;
+ int eap_tls_seq = -1;
+ guint32 eap_reass_cookie = 0;
+ gboolean needs_reassembly = FALSE;
+
+ more_fragments = test_flag(flags,EAP_TLS_FLAG_M);
+ has_length = test_flag(flags,EAP_TLS_FLAG_L);
+
+ /* Flags field, 1 byte */
+ if (tree) {
+ proto_tree_add_text(eap_tree, tvb, offset, 1, "Flags(0x%X): %s%s%s",
+ flags,
+ has_length ? "Length ":"",
+ more_fragments ? "More " :"",
+ test_flag(flags,EAP_TLS_FLAG_S) ? "Start " :"");
+ if (eap_type == EAP_TYPE_PEAP) {
+ proto_tree_add_text(eap_tree, tvb, offset, 1,
+ "PEAP version %d",
+ flags & EAP_PEAP_FLAG_VERSION);
+ }
+ }
+ size--;
+ offset++;
+
+ /* Length field, 4 bytes, OPTIONAL. */
+ if ( has_length ) {
+ length = tvb_get_ntohl(tvb, offset);
+ if (tree)
+ proto_tree_add_text(eap_tree, tvb, offset, 4, "Length: %i",length);
+ size -= 4;
+ offset += 4;
+ }
+
+ if (size>0) {
+
+ tvbuff_t *next_tvb;
+ gint tvb_len;
+ gboolean save_fragmented;
+
+ tvb_len = tvb_length_remaining(tvb, offset);
+ if (size < tvb_len)
+ tvb_len = size;
+
+ /*
+ EAP/TLS is weird protocol (it comes from
+ Microsoft after all).
+
+ If we have series of fragmented packets,
+ then there's no way of knowing that from
+ the packet itself, if it is the last packet
+ in series, that is that the packet part of
+ bigger fragmented set of data.
+
+ The only way to know is, by knowing
+ that we are already in defragmentation
+ "mode" and we are expecing packet
+ carrying fragment of data. (either
+ because we have not received expected
+ amount of data, or because the packet before
+ had "F"ragment flag set.)
+
+ The situation is alleviated by fact that it
+ is simple ack/nack protcol so there's no
+ place for out-of-order packets like it is
+ possible with IP.
+
+ Anyway, point of this lengthy essay is that
+ we have to keep state information in the
+ conversation, so that we can put ourselves in
+ defragmenting mode and wait for the last packet,
+ and have to attach state to frames as well, so
+ that we can handle defragmentation after the
+ first pass through the capture.
+ */
+ /* See if we have a remembered defragmentation EAP ID. */
+ packet_state = p_get_proto_data(pinfo->fd, proto_eap);
+ if (packet_state == NULL) {
+ /*
+ * We haven't - does this message require reassembly?
+ */
+ if (!pinfo->fd->flags.visited) {
+ /*
+ * This is the first time we've looked at this frame,
+ * so it wouldn't have any remembered information.
+ *
+ * Therefore, we check whether this conversation has
+ * a reassembly operation in progress, or whether
+ * this frame has the Fragment flag set.
+ */
+ if (conversation_state->eap_tls_seq != -1) {
+ /*
+ * There's a reassembly in progress; the sequence number
+ * of the previous fragment is
+ * "conversation_state->eap_tls_seq", and the reassembly
+ * ID is "conversation_state->eap_reass_cookie".
+ *
+ * We must include this frame in the reassembly.
+ * We advance the sequence number, giving us the
+ * sequence number for this fragment.
+ */
+ needs_reassembly = TRUE;
+ conversation_state->eap_tls_seq++;
+
+ eap_reass_cookie = conversation_state->eap_reass_cookie;
+ eap_tls_seq = conversation_state->eap_tls_seq;
+ } else if (more_fragments && has_length) {
+ /*
+ * This message has the Fragment flag set, so it requires
+ * reassembly. It's the message containing the first
+ * fragment (if it's a later fragment, the sequence
+ * number in the conversation state would not be -1).
+ *
+ * If it doesn't include a length, however, we can't
+ * do reassembly (either the message is in error, as
+ * the first fragment *must* contain a length, or we
+ * didn't capture the first fragment, and this just
+ * happens to be the first fragment we saw), so we
+ * also check that we have a length;
+ */
+ needs_reassembly = TRUE;
+ conversation_state->eap_reass_cookie = pinfo->fd->num;
+
+ /*
+ * Start the reassembly sequence number at 0.
+ */
+ conversation_state->eap_tls_seq = 0;
+
+ eap_tls_seq = conversation_state->eap_tls_seq;
+ eap_reass_cookie = conversation_state->eap_reass_cookie;
+ }
+
+ if (needs_reassembly) {
+ /*
+ * This frame requires reassembly; remember the reassembly
+ * ID for subsequent accesses to it.
+ */
+ packet_state = g_mem_chunk_alloc(frame_state_chunk);
+ packet_state->info = eap_reass_cookie;
+ p_add_proto_data(pinfo->fd, proto_eap, packet_state);
+ }
+ }
+ } else {
+ /*
+ * This frame has a reassembly cookie associated with it, so
+ * it requires reassembly. We've already done the
+ * reassembly in the first pass, so "fragment_add_seq()"
+ * won't look at the sequence number; set it to 0.
+ *
+ * XXX - a frame isn't supposed to have more than one
+ * EAP message in it, but if it includes both an EAP-TLS
+ * message and a LEAP message, we might be mistakenly
+ * concluding it requires reassembly because the "info"
+ * field isn't -1. We could, I guess, pack both EAP-TLS
+ * ID and LEAP state into the structure, but that doesn't
+ * work if you have multiple EAP-TLS or LEAP messages in
+ * the frame.
+ *
+ * But it's not clear how much work we should do to handle
+ * a bogus message such as that; as long as we don't crash
+ * or do something else equally horrible, we may not
+ * have to worry about this at all.
+ */
+ needs_reassembly = TRUE;
+ eap_reass_cookie = packet_state->info;
+ eap_tls_seq = 0;
+ }
+
+ /*
+ We test here to see whether EAP-TLS packet
+ carry fragmented of TLS data.
+
+ If this is the case, we do reasembly below,
+ otherwise we just call dissector.
+ */
+ if (needs_reassembly) {
+ fragment_data *fd_head = NULL;
+
+ /*
+ * Yes, this frame contains a fragment that requires
+ * reassembly.
+ */
+ save_fragmented = pinfo->fragmented;
+ pinfo->fragmented = TRUE;
+ fd_head = fragment_add_seq(tvb, offset, pinfo,
+ eap_reass_cookie,
+ eaptls_fragment_table,
+ eap_tls_seq,
+ size,
+ more_fragments);
+
+ if (fd_head != NULL) /* Reassembled */
+ {
+
+ next_tvb = tvb_new_real_data(fd_head->data,
+ fd_head->len,
+ fd_head->len);
+ tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+ add_new_data_source(pinfo, next_tvb, "Reassembled EAP-TLS");
+
+ show_fragment_seq_tree(fd_head, &eaptls_frag_items,
+ eap_tree, pinfo, next_tvb);
+
+ call_dissector(ssl_handle, next_tvb, pinfo, eap_tree);
+
+ /*
+ * We're finished reassembing this frame.
+ * Reinitialize the reassembly state.
+ */
+ if (!pinfo->fd->flags.visited)
+ conversation_state->eap_tls_seq = -1;
+ }
+
+ pinfo->fragmented = save_fragmented;
+
+ } else { /* this data is NOT fragmented */
+ next_tvb = tvb_new_subset(tvb, offset, tvb_len, size);
+ call_dissector(ssl_handle, next_tvb, pinfo, eap_tree);
+ }
+ }
+ }
+ break; /* EAP_TYPE_TLS */
+ /*********************************************************************
+ Cisco's Lightweight EAP (LEAP)
+ http://www.missl.cs.umd.edu/wireless/ethereal/leap.txt
+ **********************************************************************/
+ case EAP_TYPE_LEAP:
+ {
+ guint8 field,count,namesize;
+
+ /* Version (byte) */
+ if (tree) {
+ field = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(eap_tree, tvb, offset, 1,
+ "Version: %i",field);
+ }
+ size--;
+ offset++;
+
+ /* Unused (byte) */
+ if (tree) {
+ field = tvb_get_guint8(tvb, offset);
+ proto_tree_add_text(eap_tree, tvb, offset, 1,
+ "Reserved: %i",field);
+ }
+ size--;
+ offset++;
+
+ /* Count (byte) */
+ count = tvb_get_guint8(tvb, offset);
+ if (tree) {
+ proto_tree_add_text(eap_tree, tvb, offset, 1,
+ "Count: %i",count);
+ }
+ size--;
+ offset++;
+
+ /* Data (byte*Count) */
+ /* This part is state-dependent. */
+
+ /* See if we've already remembered the state. */
+ packet_state = p_get_proto_data(pinfo->fd, proto_eap);
+ if (packet_state == NULL) {
+ /*
+ * We haven't - compute the state based on the current
+ * state in the conversation.
+ */
+ leap_state = conversation_state->leap_state;
+
+ /* Advance the state machine. */
+ if (leap_state==0) leap_state = 1; else
+ if (leap_state==1) leap_state = 2; else
+ if (leap_state==2) leap_state = 3; else
+ if (leap_state==3) leap_state = 4; else
+ if (leap_state==4) leap_state = -1;
+
+ /*
+ * Remember the state for subsequent accesses to this
+ * frame.
+ */
+ packet_state = g_mem_chunk_alloc(frame_state_chunk);
+ packet_state->info = leap_state;
+ p_add_proto_data(pinfo->fd, proto_eap, packet_state);
+
+ /*
+ * Update the conversation's state.
+ */
+ conversation_state->leap_state = leap_state;
+ }
+
+ /* Get the remembered state. */
+ leap_state = packet_state->info;
+
+ if (tree) {
+
+ if (leap_state==1) {
+ proto_tree_add_text(eap_tree, tvb, offset, count,
+ "Peer Challenge [8] Random Value:\"%s\"",
+ tvb_bytes_to_str(tvb, offset, count));
+ } else if (leap_state==2) {
+ proto_tree_add_text(eap_tree, tvb, offset, count,
+ "Peer Response [24] NtChallengeResponse(%s)",
+ tvb_bytes_to_str(tvb, offset, count));
+ } else if (leap_state==3) {
+ proto_tree_add_text(eap_tree, tvb, offset, count,
+ "AP Challenge [8] Random Value:\"%s\"",
+ tvb_bytes_to_str(tvb, offset, count));
+ } else if (leap_state==4) {
+ proto_tree_add_text(eap_tree, tvb, offset, count,
+ "AP Response [24] ChallengeResponse(%s)",
+ tvb_bytes_to_str(tvb, offset, count));
+ } else {
+ proto_tree_add_text(eap_tree, tvb, offset, count,
+ "Data (%d byte%s): \"%s\"",
+ count, plurality(count, "", "s"),
+ tvb_bytes_to_str(tvb, offset, count));
+ }
+
+ } /* END: if (tree) */
+
+
+ size -= count;
+ offset += count;
+
+ /* Name (Length-(8+Count)) */
+ namesize = eap_len - (8+count);
+ if (tree) {
+ proto_tree_add_text(eap_tree, tvb, offset, namesize,
+ "Name (%d byte%s): %s",
+ namesize, plurality(count, "", "s"),
+ tvb_format_text(tvb, offset, namesize));
+ }
+ size -= namesize;
+ offset += namesize;
+ }
+
+ break; /* EAP_TYPE_LEAP */
+ /*********************************************************************
+ EAP-MSCHAPv2 - draft-kamath-pppext-eap-mschapv2-00.txt
+ **********************************************************************/
+ case EAP_TYPE_MSCHAPV2:
+ if (tree)
+ dissect_eap_mschapv2(eap_tree, tvb, offset, size);
+ break; /* EAP_TYPE_MSCHAPV2 */
+ /*********************************************************************
+ EAP-SIM - draft-haverinen-pppext-eap-sim-12.txt
+ **********************************************************************/
+ case EAP_TYPE_SIM:
+ if (tree)
+ dissect_eap_sim(eap_tree, tvb, offset, size);
+ break; /* EAP_TYPE_SIM */
+ /*********************************************************************
+ **********************************************************************/
+ default:
+ if (tree) {
+ proto_tree_add_text(eap_tree, tvb, offset, size,
+ "Type-Data (%d byte%s) Value: %s",
+ size, plurality(size, "", "s"),
+ tvb_bytes_to_str(tvb, offset, size));
+ }
+ break;
+ /*********************************************************************
+ **********************************************************************/
+ } /* switch (eap_type) */
+
+ }
+
+ } /* switch (eap_code) */
+
+ return tvb_length(tvb);
+}
+
+static int
+dissect_eap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ return dissect_eap_data(tvb, pinfo, tree, FALSE);
+}
+
+static int
+dissect_eap_fragment(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ return dissect_eap_data(tvb, pinfo, tree, TRUE);
+}
+
+void
+proto_register_eap(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_eap_code, {
+ "Code", "eap.code", FT_UINT8, BASE_DEC,
+ VALS(eap_code_vals), 0x0, "", HFILL }},
+ { &hf_eap_identifier, {
+ "Id", "eap.id", FT_UINT8, BASE_DEC,
+ NULL, 0x0, "", HFILL }},
+ { &hf_eap_len, {
+ "Length", "eap.len", FT_UINT16, BASE_DEC,
+ NULL, 0x0, "", HFILL }},
+ { &hf_eap_type, {
+ "Type", "eap.type", FT_UINT8, BASE_DEC,
+ VALS(eap_type_vals), 0x0, "", HFILL }},
+ { &hf_eap_type_nak, {
+ "Desired Auth Type", "eap.desired_type", FT_UINT8, BASE_DEC,
+ VALS(eap_type_vals), 0x0, "", HFILL }},
+ { &hf_eaptls_fragment,
+ { "EAP-TLS Fragment", "eaptls.fragment",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "EAP-TLS Fragment", HFILL }},
+ { &hf_eaptls_fragments,
+ { "EAP-TLS Fragments", "eaptls.fragments",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ "EAP-TLS Fragments", HFILL }},
+ { &hf_eaptls_fragment_overlap,
+ { "Fragment overlap", "eaptls.fragment.overlap",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment overlaps with other fragments", HFILL }},
+ { &hf_eaptls_fragment_overlap_conflict,
+ { "Conflicting data in fragment overlap", "eaptls.fragment.overlap.conflict",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Overlapping fragments contained conflicting data", HFILL }},
+ { &hf_eaptls_fragment_multiple_tails,
+ { "Multiple tail fragments found", "eaptls.fragment.multipletails",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Several tails were found when defragmenting the packet", HFILL }},
+ { &hf_eaptls_fragment_too_long_fragment,
+ { "Fragment too long", "eaptls.fragment.toolongfragment",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment contained data past end of packet", HFILL }},
+ { &hf_eaptls_fragment_error,
+ { "Defragmentation error", "eaptls.fragment.error",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "Defragmentation error due to illegal fragments", HFILL }},
+ };
+ static gint *ett[] = {
+ &ett_eap,
+ &ett_eaptls_fragment,
+ &ett_eaptls_fragments,
+ &ett_eap_sim_attr,
+ };
+
+ proto_eap = proto_register_protocol("Extensible Authentication Protocol",
+ "EAP", "eap");
+ proto_register_field_array(proto_eap, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+ register_init_routine(&eap_init_protocol);
+
+ new_register_dissector("eap", dissect_eap, proto_eap);
+ new_register_dissector("eap_fragment", dissect_eap_fragment, proto_eap);
+ register_init_routine(eaptls_defragment_init);
+}
+
+void
+proto_reg_handoff_eap(void)
+{
+ dissector_handle_t eap_handle;
+
+ /*
+ * Get a handle for the SSL/TLS dissector.
+ */
+ ssl_handle = find_dissector("ssl");
+
+ eap_handle = find_dissector("eap");
+ dissector_add("ppp.protocol", PPP_EAP, eap_handle);
+}