/* packet-teap.c
 * Routines for TEAP (Tunnel Extensible Authentication Protocol)
 * RFC 7170
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/proto_data.h>

void proto_register_teap(void);
void proto_reg_handoff_teap(void);

static int proto_teap = -1;

static gint ett_teap = -1;
static gint ett_teap_tlv = -1;
static gint ett_pac_attr_tlv = -1;

static expert_field ei_teap_bad_length = EI_INIT;

static dissector_handle_t teap_handle;

static dissector_handle_t eap_handle;

/*
  From RFC7170, pg 27

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |M|R|            TLV Type       |            Length             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              Value...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

#define TEAP_TLV_MANDATORY 0x8000
#define TEAP_TLV_RESERVED  0x4000
#define TEAP_TLV_TYPE      0x3FFF

#define TEAP_CRYPTO_FLAGS    0xF0
#define TEAP_CRYPTO_SUBTYPE  0x0F

#define TEAP_UNASSIGNED              0
#define TEAP_AUTHORITY_ID            1
#define TEAP_IDENTITY                2
#define TEAP_RESULT                  3
#define TEAP_NAK                     4
#define TEAP_ERROR                   5
#define TEAP_CHANNEL_BINDING         6
#define TEAP_VENDOR_SPECIFIC         7
#define TEAP_REQUEST_ACTION          8
#define TEAP_EAP_PAYLOAD             9
#define TEAP_INTERMEDIATE_RESULT     10
#define TEAP_PAC                     11
#define TEAP_CRYPTO_BINDING          12
#define TEAP_BASIC_PWD_AUTH_REQUEST  13
#define TEAP_BASIC_PWD_AUTH_RESPONSE 14
#define TEAP_PKCS7                   15
#define TEAP_PKCS10                  16
#define TEAP_TRUSTED_SERVER_ROOT     17

static const value_string teap_tlv_type_vals[] = {
   { TEAP_UNASSIGNED,              "Unassigned" },
   { TEAP_AUTHORITY_ID,            "Authority-ID" },
   { TEAP_IDENTITY,                "Identity-Type" },
   { TEAP_RESULT,                  "Result" },
   { TEAP_NAK,                     "NAK" },
   { TEAP_ERROR,                   "Error" },
   { TEAP_CHANNEL_BINDING,         "Channel-Binding" },
   { TEAP_VENDOR_SPECIFIC,         "Vendor-Specific" },
   { TEAP_REQUEST_ACTION,          "Request-Action" },
   { TEAP_EAP_PAYLOAD,             "EAP-Payload" },
   { TEAP_INTERMEDIATE_RESULT,     "Intermediate-Result" },
   { TEAP_PAC,                     "PAC" },
   { TEAP_CRYPTO_BINDING,          "Crypto-Binding" },
   { TEAP_BASIC_PWD_AUTH_REQUEST,  "Basic-Password-Auth-Req" },
   { TEAP_BASIC_PWD_AUTH_RESPONSE, "Basic-Password-Auth-Resp" },
   { TEAP_PKCS7,                   "PKCS#7" },
   { TEAP_PKCS10,                  "PKCS#10" },
   { TEAP_TRUSTED_SERVER_ROOT,     "Trusted-Server-Root" },
   { 0,                            NULL }
 };

static const value_string teap_identity_vals[] = {
   { 1, "User" },
   { 2, "Machine" },
   { 0, NULL }
 };

static const value_string teap_status_vals[] = {
   { 1, "Success" },
   { 2, "Failure" },
   { 0, NULL }
 };

static const value_string teap_request_action_status_vals[] = {
   { 1, "Success" },
   { 2, "Failure" },
   { 0, NULL }
 };

static const value_string teap_request_action_action_vals[] = {
   { 1, "Process-TLV" },
   { 2, "Negotiate-EAP" },
   { 0, NULL }
 };

 #define FLAG_EMSK_PRESENT 1
 #define FLAG_MSK_PRESENT  2
 #define FLAG_BOTH_PRESENT 3

static const value_string teap_crypto_flags_vals[] = {
   { FLAG_EMSK_PRESENT, "EMSK Compound MAC is present" },
   { FLAG_MSK_PRESENT,  "MSK Compound MAC is present" },
   { FLAG_BOTH_PRESENT, "Both EMSK and MSK Compound MAC are present" },
   { 0, NULL }
};

static const value_string teap_crypto_subtype_vals[] = {
   { 0, "Binding Request" },
   { 1, "Binding Response" },
   { 0, NULL }
};

static const value_string teap_error_code_vals[] = {
   { 1,    "User account expires soon" },
   { 2,    "User account credential expires soon" },
   { 3,    "User account authorizations change soon" },
   { 4,    "Clock skew detected" },
   { 5,    "Contact administrator" },
   { 6,    "User account credentials change required" },
   { 1001, "Inner Method Error" },
   { 1002, "Unspecified authentication infrastructure problem" },
   { 1003, "Unspecified authentication failure" },
   { 1004, "Unspecified authorization failure" },
   { 1005, "User account credentials unavailable" },
   { 1006, "User account expired" },
   { 1007, "User account locked: try again later" },
   { 1008, "User account locked: admin intervention required" },
   { 1009, "Authentication infrastructure unavailable" },
   { 1010, "Authentication infrastructure not trusted" },
   { 1011, "Clock skew too great" },
   { 1012, "Invalid inner realm" },
   { 1013, "Token out of sync: administrator intervention required" },
   { 1014, "Token out of sync: PIN change required" },
   { 1015, "Token revoked" },
   { 1016, "Tokens exhausted" },
   { 1017, "Challenge expired" },
   { 1018, "Challenge algorithm mismatch" },
   { 1019, "Client certificate not supplied" },
   { 1020, "Client certificate rejected" },
   { 1021, "Realm mismatch between inner and outer identity" },
   { 1022, "Unsupported Algorithm In Certificate Signing Request" },
   { 1023, "Unsupported Extension In Certificate Signing Request" },
   { 1024, "Bad Identity In Certificate Signing Request" },
   { 1025, "Bad Certificate Signing Request" },
   { 1026, "Internal CA Error" },
   { 1027, "General PKI Error" },
   { 1028, "Inner method's channel-binding data required but not supplied" },
   { 1029, "Inner method's channel-binding data did not include required information" },
   { 1030, "Inner method's channel binding failed" },
   { 1031, "User account credentials incorrect [USAGE NOT RECOMMENDED]" },
   { 2001, "Tunnel Compromise Error" },
   { 2002, "Unexpected TLVs Exchanged" },
   { 0, NULL }
};

#define PAC_KEY       1
#define PAC_OPAQUE    2
#define PAC_LIFETIME  3
#define PAC_A_ID      4
#define PAC_I_ID      5
#define PAC_RESERVED  6
#define PAC_A_ID_INFO 7
#define PAC_ACK       8
#define PAC_INFO      9
#define PAC_TYPE      10

static const value_string pac_attr_type_vals[] = {
   { PAC_KEY,       "PAC-Key" },
   { PAC_OPAQUE,    "PAC-Opaque" },
   { PAC_LIFETIME,  "PAC-Lifetime" },
   { PAC_A_ID,      "A-ID" },
   { PAC_I_ID,      "I-ID" },
   { PAC_RESERVED,  "Reserved" },
   { PAC_A_ID_INFO, "A-ID-Info" },
   { PAC_ACK,       "PAC-Acknowledgement" },
   { PAC_INFO,      "PAC-Info" },
   { PAC_TYPE,      "PAC-Type" },
   { 0,             NULL }
};

static const value_string pac_result_vals[] = {
   { 1, "Success" },
   { 2, "Failure" },
   { 0, NULL }
 };

static const value_string pac_type_vals[] = {
   { 1, "Tunnel PAC" },
   { 0, NULL }
 };

static int hf_teap_tlv_mandatory = -1;
static int hf_teap_tlv_reserved = -1;
static int hf_teap_tlv_type = -1;
static int hf_teap_tlv_len = -1;
static int hf_teap_tlv_val = -1;
static int hf_teap_auth_id = -1;
static int hf_teap_identity = -1;
static int hf_teap_status = -1;
static int hf_teap_vendor_id = -1;
static int hf_teap_request_action_status = -1;
static int hf_teap_request_action_action = -1;
static int hf_teap_crypto_reserved = -1;
static int hf_teap_crypto_version = -1;
static int hf_teap_crypto_rcv_version = -1;
static int hf_teap_crypto_flags = -1;
static int hf_teap_crypto_subtype = -1;
static int hf_teap_crypto_nonce = -1;
static int hf_teap_crypto_emsk = -1;
static int hf_teap_crypto_msk = -1;
static int hf_teap_nak_type = -1;
static int hf_teap_error_code = -1;
static int hf_teap_prompt = -1;
static int hf_teap_user_len = -1;
static int hf_teap_username = -1;
static int hf_teap_pass_len = -1;
static int hf_teap_password = -1;

static int hf_pac_attr_type = -1;
static int hf_pac_attr_pac_key = -1;
static int hf_pac_attr_pac_opaque = -1;
static int hf_pac_attr_pac_lifetime = -1;
static int hf_pac_attr_pac_a_id = -1;
static int hf_pac_attr_pac_i_id = -1;
static int hf_pac_attr_pac_reserved = -1;
static int hf_pac_attr_pac_a_id_info = -1;
static int hf_pac_attr_pac_result = -1;
static int hf_pac_attr_pac_type = -1;
static int hf_pac_attr_val = -1;

static int
dissect_teap_tlv_pac(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint16 len);

static int
dissect_pac_attr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
{
  guint16 type;
  guint16 len;
  int start_offset = offset;

  type = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
  len = tvb_get_guint16(tvb, offset + 2, ENC_BIG_ENDIAN);

  proto_tree_add_item(tree, hf_pac_attr_type, tvb, offset, 2, ENC_BIG_ENDIAN);
  offset += 2;

  proto_tree_add_item(tree, hf_teap_tlv_len, tvb, offset, 2, ENC_BIG_ENDIAN);
  offset += 2;

  switch (type) {
    case PAC_KEY:
      proto_tree_add_item(tree, hf_pac_attr_pac_key, tvb, offset, len, ENC_NA);
      offset += len;
      break;

    case PAC_OPAQUE:
      proto_tree_add_item(tree, hf_pac_attr_pac_opaque, tvb, offset, len, ENC_NA);
      offset += len;
      break;

    case PAC_LIFETIME:
      proto_tree_add_item(tree, hf_pac_attr_pac_lifetime, tvb, offset, 4, ENC_NA);
      offset += 4;
      break;

    case PAC_A_ID:
      proto_tree_add_item(tree, hf_pac_attr_pac_a_id, tvb, offset, len, ENC_ASCII | ENC_NA);
      offset += len;
      break;

    case PAC_I_ID:
      proto_tree_add_item(tree, hf_pac_attr_pac_i_id, tvb, offset, len, ENC_ASCII | ENC_NA);
      offset += len;
      break;

    case PAC_RESERVED:
      proto_tree_add_item(tree, hf_pac_attr_pac_reserved, tvb, offset, len, ENC_NA);
      offset += len;
      break;

    case PAC_A_ID_INFO:
      proto_tree_add_item(tree, hf_pac_attr_pac_a_id_info, tvb, offset, len, ENC_ASCII | ENC_NA);
      offset += len;
      break;

    case PAC_ACK:
      proto_tree_add_item(tree, hf_pac_attr_pac_result, tvb, offset, len, ENC_NA);
      offset += len;
      break;

    case PAC_INFO:
      offset += dissect_teap_tlv_pac(tvb, pinfo, tree, offset, len);
      break;

    case PAC_TYPE:
      proto_tree_add_item(tree, hf_pac_attr_pac_type, tvb, offset, len, ENC_NA);
      offset += len;
      break;

    default:
      proto_tree_add_item(tree, hf_pac_attr_val, tvb, offset, len, ENC_NA);
      offset += len;
      break;
  }
  return offset - start_offset;
}

static int
dissect_teap_tlv_pac(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint16 len)
{
  int start_offset = offset;

  while (offset - start_offset < len) {
    offset += dissect_pac_attr(tvb, pinfo, tree, offset);
  }
  return offset - start_offset;
}

static int
dissect_teap_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, gboolean top)
{
  int start_offset = offset;
  guint16 type;
  guint16 len;
  proto_tree *tlv_tree;
  proto_tree *ti_len;
  tvbuff_t *next_tvb;

  type = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN) & TEAP_TLV_TYPE;
  len = tvb_get_guint16(tvb, offset + 2, ENC_BIG_ENDIAN);

  tlv_tree = proto_tree_add_subtree_format(tree, tvb, offset, 4 + len,
      ett_teap_tlv, NULL, "TLV %s (%u): ",
      val_to_str_const(type, teap_tlv_type_vals, "Unknown"), type);

  proto_tree_add_item(tlv_tree, hf_teap_tlv_mandatory, tvb, offset, 2, ENC_BIG_ENDIAN);
  proto_tree_add_item(tlv_tree, hf_teap_tlv_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
  proto_tree_add_item(tlv_tree, hf_teap_tlv_type, tvb, offset, 2, ENC_BIG_ENDIAN);
  offset += 2;

  proto_tree_add_item(tlv_tree, hf_teap_tlv_len, tvb, offset, 2, ENC_BIG_ENDIAN);
  offset += 2;

  if (top) {
    col_add_str(pinfo->cinfo, COL_INFO,
        val_to_str(type, teap_tlv_type_vals, "Unknown TLV (0x%02X)"));
  }
  switch (type) {
    case TEAP_AUTHORITY_ID:
      proto_tree_add_item(tlv_tree, hf_teap_auth_id, tvb, offset, len, ENC_NA);
      offset += len;
      break;

    case TEAP_IDENTITY:
      proto_tree_add_item(tlv_tree, hf_teap_identity, tvb, offset, 2, ENC_BIG_ENDIAN);
      offset += len;
      break;

    case TEAP_RESULT:
      proto_tree_add_item(tlv_tree, hf_teap_status, tvb, offset, 2, ENC_BIG_ENDIAN);
      offset += len;
      break;

    case TEAP_NAK:
      proto_tree_add_item(tlv_tree, hf_teap_vendor_id, tvb, offset, 4, ENC_BIG_ENDIAN);
      offset += 4;
      proto_tree_add_item(tlv_tree, hf_teap_nak_type, tvb, offset, 2, ENC_BIG_ENDIAN);
      offset += 2;
      break;

    case TEAP_ERROR:
      proto_tree_add_item(tlv_tree, hf_teap_error_code, tvb, offset, 4, ENC_BIG_ENDIAN);
      offset += len;
      break;

    case TEAP_VENDOR_SPECIFIC:
      proto_tree_add_item(tlv_tree, hf_teap_vendor_id, tvb, offset, 4, ENC_BIG_ENDIAN);
      offset += len;
      break;

    case TEAP_REQUEST_ACTION:
      proto_tree_add_item(tlv_tree, hf_teap_request_action_status, tvb, offset, 1, ENC_BIG_ENDIAN);
      offset += 1;
      proto_tree_add_item(tlv_tree, hf_teap_request_action_action, tvb, offset, 1, ENC_BIG_ENDIAN);
      offset += 1;
      break;

    case TEAP_EAP_PAYLOAD:
      next_tvb = tvb_new_subset_length(tvb, offset, len);
      call_dissector(eap_handle, next_tvb, pinfo, tlv_tree);
      offset += len;
      break;

    case TEAP_INTERMEDIATE_RESULT:
      proto_tree_add_item(tlv_tree, hf_teap_status, tvb, offset, 2, ENC_BIG_ENDIAN);
      offset += len;
      break;

    case TEAP_PAC:
      offset += dissect_teap_tlv_pac(tvb, pinfo, tlv_tree, offset, len);
      break;

    case TEAP_CRYPTO_BINDING:
    {
      guint8 flags;
      proto_tree_add_item(tlv_tree, hf_teap_crypto_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
      offset += 1;
      proto_tree_add_item(tlv_tree, hf_teap_crypto_version, tvb, offset, 1, ENC_BIG_ENDIAN);
      offset += 1;
      proto_tree_add_item(tlv_tree, hf_teap_crypto_rcv_version, tvb, offset, 1, ENC_BIG_ENDIAN);
      offset += 1;
      flags = (tvb_get_guint8(tvb, offset) & TEAP_CRYPTO_FLAGS) >> 4;
      proto_tree_add_item(tlv_tree, hf_teap_crypto_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
      proto_tree_add_item(tlv_tree, hf_teap_crypto_subtype, tvb, offset, 1, ENC_BIG_ENDIAN);
      offset += 1;
      proto_tree_add_item(tlv_tree, hf_teap_crypto_nonce, tvb, offset, 32, ENC_NA);
      offset += 32;
      if (flags == FLAG_EMSK_PRESENT || flags == FLAG_BOTH_PRESENT) {
        proto_tree_add_item(tlv_tree, hf_teap_crypto_emsk, tvb, offset, 20, ENC_NA);
      }
      offset += 20;
      if (flags == FLAG_MSK_PRESENT || flags == FLAG_BOTH_PRESENT) {
        proto_tree_add_item(tlv_tree, hf_teap_crypto_msk, tvb, offset, 20, ENC_NA);
      }
      offset += 20;
    }
    break;

    case TEAP_BASIC_PWD_AUTH_REQUEST:
      if (len > 0) {
        proto_tree_add_item(tlv_tree, hf_teap_prompt, tvb, offset, len, ENC_ASCII | ENC_NA);
        offset += len;
      }
      break;

    case TEAP_BASIC_PWD_AUTH_RESPONSE:
    {
      guint8 auth_len;
      auth_len = tvb_get_guint8(tvb, offset);
      proto_tree_add_item(tlv_tree, hf_teap_user_len, tvb, offset, 1, ENC_BIG_ENDIAN);
      offset += 1;
      proto_tree_add_item(tlv_tree, hf_teap_username, tvb, offset, auth_len, ENC_ASCII | ENC_NA);
      offset += auth_len;

      auth_len = tvb_get_guint8(tvb, offset);
      proto_tree_add_item(tlv_tree, hf_teap_pass_len, tvb, offset, 1, ENC_BIG_ENDIAN);
      offset += 1;
      proto_tree_add_item(tlv_tree, hf_teap_password, tvb, offset, auth_len, ENC_ASCII | ENC_NA);
      offset += auth_len;
    }
    break;

    case TEAP_CHANNEL_BINDING:
    case TEAP_TRUSTED_SERVER_ROOT:
    case TEAP_PKCS7:
    case TEAP_PKCS10:
    default:
      ti_len = proto_tree_add_item(tlv_tree, hf_teap_tlv_val, tvb, offset, len, ENC_NA);
      if ((guint)len + 4 > tvb_reported_length(tvb)) {
        expert_add_info(pinfo, ti_len, &ei_teap_bad_length);
      }
      offset += len;
      break;
  }

  return offset - start_offset;
}

static int
dissect_teap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
  proto_tree *ti;
  proto_tree *teap_tree;
  int offset = 0;

  col_set_str(pinfo->cinfo, COL_PROTOCOL, "TEAP");
  col_clear(pinfo->cinfo, COL_INFO);

  ti = proto_tree_add_item(tree, proto_teap, tvb, 0, tvb_captured_length(tvb), ENC_NA);
  teap_tree = proto_item_add_subtree(ti, ett_teap);

  while (offset < (int)tvb_captured_length(tvb)) {
    offset += dissect_teap_tlv(tvb, pinfo, teap_tree, offset, offset == 0);
  }

  return tvb_captured_length(tvb);
}

void
proto_register_teap(void)
{
  static hf_register_info hf[] = {
    { &hf_teap_tlv_mandatory, {
      "Mandatory", "teap.tlv.mandatory",
      FT_BOOLEAN, 16, NULL, TEAP_TLV_MANDATORY,
      NULL, HFILL }},

    { &hf_teap_tlv_reserved, {
      "Reserved", "teap.tlv.reserved",
      FT_UINT16, BASE_DEC, NULL, TEAP_TLV_RESERVED,
      NULL, HFILL }},

     { &hf_teap_tlv_type, {
      "Type", "teap.tlv.type",
      FT_UINT16, BASE_DEC, VALS(teap_tlv_type_vals), TEAP_TLV_TYPE,
      NULL, HFILL }},

     { &hf_teap_tlv_len, {
      "Length", "teap.tlv.len",
      FT_UINT16, BASE_DEC, NULL, 0x00,
      NULL, HFILL }},

     { &hf_teap_auth_id, {
      "ID", "teap.authority-id",
      FT_BYTES, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_identity, {
      "Identity", "teap.identity",
      FT_UINT16, BASE_DEC, VALS(teap_identity_vals), 0x0,
      NULL, HFILL }},

     { &hf_teap_status, {
      "Status", "teap.status",
      FT_UINT16, BASE_DEC, VALS(teap_status_vals), 0x0,
      NULL, HFILL }},

     { &hf_teap_vendor_id, {
      "Vendor-Id", "teap.vendor-id",
      FT_UINT32, BASE_HEX, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_crypto_reserved, {
      "Reserved", "teap.crypto.reserved",
      FT_UINT8, BASE_DEC, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_crypto_version, {
      "Version", "teap.crypto.version",
      FT_UINT8, BASE_DEC, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_crypto_rcv_version, {
      "Received Version", "teap.crypto.received-version",
      FT_UINT8, BASE_DEC, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_crypto_flags, {
      "Flags", "teap.crypto.flags",
      FT_UINT8, BASE_DEC, VALS(teap_crypto_flags_vals), TEAP_CRYPTO_FLAGS,
      NULL, HFILL }},

     { &hf_teap_crypto_subtype, {
      "Subtype", "teap.crypto.subtype",
      FT_UINT8, BASE_DEC, VALS(teap_crypto_subtype_vals), TEAP_CRYPTO_SUBTYPE,
      NULL, HFILL }},

     { &hf_teap_crypto_nonce, {
      "Nonce", "teap.crypto.nonce",
      FT_BYTES, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_crypto_emsk, {
      "EMSK Compound MAC", "teap.crypto.emsk",
      FT_BYTES, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_crypto_msk, {
      "MSK Compound MAC", "teap.crypto.msk",
      FT_BYTES, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_nak_type, {
      "NAK-Type", "teap.nak-type",
      FT_UINT16, BASE_HEX, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_error_code, {
      "Error-Code", "teap.error-code",
      FT_UINT32, BASE_DEC, VALS(teap_error_code_vals), 0x0,
      NULL, HFILL }},

     { &hf_teap_request_action_action, {
      "Action", "teap.request-action.action",
      FT_UINT8, BASE_DEC, VALS(teap_request_action_action_vals), 0x0,
      NULL, HFILL }},

     { &hf_teap_request_action_status, {
      "Status", "teap.request-action.status",
      FT_UINT8, BASE_DEC, VALS(teap_request_action_status_vals), 0x0,
      NULL, HFILL }},

     { &hf_teap_prompt, {
      "Prompt", "teap.prompt",
      FT_STRING, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_user_len, {
      "Userlen", "teap.user_len",
      FT_UINT8, BASE_DEC, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_username, {
      "Username", "teap.username",
      FT_STRING, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_pass_len, {
      "Passlen", "teap.pass_len",
      FT_UINT8, BASE_DEC, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_password, {
      "Password", "teap.password",
      FT_STRING, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_teap_tlv_val, {
      "Value", "teap.tlv.val",
      FT_BYTES, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_type, {
      "Type", "teap.pac.type",
      FT_UINT16, BASE_DEC, VALS(pac_attr_type_vals), 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_pac_key, {
      "Key", "teap.pac.key",
      FT_BYTES, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_pac_opaque, {
      "Opaque", "teap.pac.opaque",
      FT_BYTES, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_pac_lifetime, {
      "Lifetime", "teap.pac.lifetime",
      FT_UINT32, BASE_DEC, NULL, 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_pac_a_id, {
      "A-ID", "teap.pac.a-id",
      FT_STRING, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_pac_i_id, {
      "I-ID", "teap.pac.i-id",
      FT_STRING, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_pac_reserved, {
      "Reserved", "teap.pac.reserved",
      FT_BYTES, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_pac_a_id_info, {
      "A-ID-Info", "teap.pac.a-id-info",
      FT_STRING, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_pac_result, {
      "Type", "teap.pac.result",
      FT_UINT16, BASE_DEC, VALS(pac_result_vals), 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_pac_type, {
      "Type", "teap.pac.pac-type",
      FT_UINT16, BASE_DEC, VALS(pac_type_vals), 0x0,
      NULL, HFILL }},

     { &hf_pac_attr_val, {
      "Value", "teap.pac.val",
      FT_BYTES, BASE_NONE, NULL, 0x0,
      NULL, HFILL }},
  };

  static gint *ett[] = {
    &ett_teap,
    &ett_teap_tlv,
    &ett_pac_attr_tlv,
  };
  static ei_register_info ei[] = {
     { &ei_teap_bad_length, { "teap.bad_length", PI_PROTOCOL, PI_WARN, "Bad length (too large)", EXPFILL }},
  };

  expert_module_t* expert_teap;

  proto_teap = proto_register_protocol("Tunnel Extensible Authentication Protocol",
                                       "TEAP", "teap");
  proto_register_field_array(proto_teap, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));
  expert_teap = expert_register_protocol(proto_teap);
  expert_register_field_array(expert_teap, ei, array_length(ei));

  teap_handle = register_dissector("teap", dissect_teap, proto_teap);
}

void
proto_reg_handoff_teap(void)
{
  eap_handle = find_dissector_add_dependency("eap", proto_teap);
}
/*
 * Editor modelines
 *
 * Local Variables:
 * c-basic-offset: 2
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * ex: set shiftwidth=2 tabstop=8 expandtab:
 * :indentSize=2:tabSize=8:noTabs=true:
 */
