Ethereal-dev: [ethereal-dev] ldap dissector

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

Date: Tue, 21 Mar 2000 04:19:50 -0500 (EST)
I finally got fed up with the current ldap dissector (it doesn't
dissect <g>).

I've spent the evening getting up to speed on how to write a dissector,
and have produced a first pass at handling the ldap messages. It only
handles to following right now

bind request
bind response
search request (partially)
search result response

but adding the others is fairly easy now.

Before I go any further, I'd like to ask for comments on the code.

-- 
Doug Nazar
Dragon Computer Consultants Inc.
Tel: (416) 708-1578     Fax: (416) 708-8081
/* packet-ldap.c
 * Routines for ldap packet dissection
 *
 * $Id: packet-ldap.c,v 1.2 2000/01/07 22:05:32 guy Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxx>
 * 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 <stdio.h>

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif

#include <string.h>
#include <glib.h>
#include "packet.h"

#include "packet-ldap.h"

static int proto_ldap = -1;
static int hf_ldap_length = -1;
static int hf_ldap_message_id = -1;
static int hf_ldap_message_type = -1;
static int hf_ldap_message_length = -1;

static int hf_ldap_message_result = -1;
static int hf_ldap_message_result_matcheddn = -1;
static int hf_ldap_message_result_errormsg = -1;
static int hf_ldap_message_result_referral = -1;

static int hf_ldap_message_bind_version = -1;
static int hf_ldap_message_bind_dn = -1;
static int hf_ldap_message_bind_auth = -1;
static int hf_ldap_message_bind_auth_password = -1;

static int hf_ldap_message_search_base = -1;
static int hf_ldap_message_search_scope = -1;
static int hf_ldap_message_search_deref = -1;
static int hf_ldap_message_search_sizeLimit = -1;
static int hf_ldap_message_search_timeLimit = -1;
static int hf_ldap_message_search_typesOnly = -1;

static gint ett_ldap = -1;
static gint ett_ldap_message = -1;
static gint ett_ldap_referrals = -1;

static value_string msgTypes [] = {
  {LDAP_REQ_BIND, "Bind Request"},
  {LDAP_REQ_UNBIND, "Unbind Request"},
  {LDAP_REQ_UNBIND_30, "Unbind Request"},
  {LDAP_REQ_SEARCH, "Search Request"},
  {LDAP_REQ_MODIFY, "Modify Request"},
  {LDAP_REQ_ADD, "Add Request"},
  {LDAP_REQ_DELETE, "Delete Request"},
  {LDAP_REQ_DELETE_30, "Delete Request"},
  {LDAP_REQ_MODRDN, "Modify RDN Request"},
  {LDAP_REQ_COMPARE, "Compare Request"},
  {LDAP_REQ_ABANDON, "Abandon Request"},
  {LDAP_REQ_ABANDON_30, "Abandon Request"},
    
  {LDAP_RES_BIND, "Bind Result"},
  {LDAP_RES_SEARCH_ENTRY, "Search Entry"},
  {LDAP_RES_SEARCH_RESULT, "Search Result"},
  {LDAP_RES_MODIFY, "Modify Result"},
  {LDAP_RES_ADD, "Add Result"},
  {LDAP_RES_DELETE, "Delete Result"},
  {LDAP_RES_MODRDN, "Modify RDN Result"},
  {LDAP_RES_COMPARE, "Compare Result"}
};

static const char *message_type_str(long messageType)
{
  int count = sizeof(msgTypes) / sizeof(value_string);
  while (count--)
  {
    if (msgTypes[count].value == messageType)
      return msgTypes[count].strptr;
  }
  
  return "Unknown";
}

static int read_length(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hf_id, long *len)
{
  long t = 0;
  const u_char *p = pd + offset;
  long length = 0;
  int item_length;

  length = t = *p++;
  if (t & 0x80)
  {
    t &= 0x7f;
    length = 0;
    while (t--)
    {
      length <<= 8;
      length |= *p++;
    }
  }
  
  if (len)
    *len = length;

  item_length = (p - pd) - offset;

  if (tree)
    proto_tree_add_item(tree, hf_id, offset, item_length, length);

  return item_length;
}

static int read_integer(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hf_id, long *i, gboolean override)
{
  int l = 0;
  const u_char *p = pd + offset;
  long integer = 0;
  int item_length;

  if (*p != LBER_INTEGER && !override)
    return 0;
  
  p++;
  l = *p++;
  integer = 0;
  
  while (l--)
  {
    integer <<= 8;
    integer |= *p++;
  }

  if (i)
    *i = integer;

  item_length = (p - pd) - offset;

  if (tree)
    proto_tree_add_item(tree, hf_id, offset, item_length, integer);

  return item_length;
}

static int read_string(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hf_id, char **s, gboolean override_type)
{
  int l = 0;
  const u_char *p = pd + offset;
  int item_length;
  char *string;

  if (*p != LBER_OCTETSTRING && !override_type)
    return 0;
  
  p++;
  l = *p++;
  item_length = l + 2;

  if (l)
  {
    string = g_malloc(l + 1);
    if (string)
    {
      memcpy(string, p, l);
      string[l] = '\0';

      if (tree)
        proto_tree_add_item(tree, hf_id, offset, item_length, string);

      if (s)
        *s = string;
      else
        g_free(string);
    }
  }
  else
  {
    if (tree)
      proto_tree_add_item(tree, hf_id, offset, item_length, "(null)");
  }

  return item_length;
}

static int dissect_ldap_result(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
  int i = offset;
  long resultCode = 0;
  
  i += read_length(pd, i, fd, tree, hf_ldap_message_length, 0);
  i += read_integer(pd, i, fd, tree, hf_ldap_message_result, &resultCode, TRUE);
  i += read_string(pd, i, fd, tree, hf_ldap_message_result_matcheddn, 0, FALSE);
  i += read_string(pd, i, fd, tree, hf_ldap_message_result_errormsg, 0, FALSE);

  if (resultCode == 10)		/* Referral */
  {
    int start = i;
    long length;
    proto_tree *t, *referralTree;
    
    i++;
    i += read_length(pd, i, fd, 0, -1, &length);
    t = proto_tree_add_text(tree, start, length, "Referral URLs");
    referralTree = proto_item_add_subtree(t, ett_ldap_referrals);
    
    while (i < (start + length))
    {
      i += read_string(pd, i, fd, referralTree, hf_ldap_message_result_referral, 0, FALSE);
    }
  }
    
  return i - offset;
}

static int dissect_ldap_request_bind(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
  int i = offset;

  i += read_length(pd, i, fd, tree, hf_ldap_message_length, 0);
  i += read_integer(pd, i, fd, tree, hf_ldap_message_bind_version, 0, FALSE);
  i += read_string(pd, i, fd, tree, hf_ldap_message_bind_dn, 0, FALSE);

  switch (pd[i])
  {
   case LDAP_AUTH_SIMPLE:
   case LDAP_AUTH_SIMPLE_30:
    proto_tree_add_item(tree, hf_ldap_message_bind_auth, i, 1, pd[i]);
    i += read_string(pd, i, fd, tree, hf_ldap_message_bind_auth_password, 0, TRUE);
  };
  
  return i - offset;
}

static int dissect_ldap_response_bind(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
  int i = offset;
  
  i += dissect_ldap_result(pd, i, fd, tree);
  
  return i - offset;
}

static int dissect_ldap_request_search(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
  int i = offset;
  
  i += read_length(pd, i, fd, tree, hf_ldap_message_length, 0);
  i += read_string(pd, i, fd, tree, hf_ldap_message_search_base, 0, FALSE);
  i += read_integer(pd, i, fd, tree, hf_ldap_message_search_scope, 0, TRUE);
  i += read_integer(pd, i, fd, tree, hf_ldap_message_search_deref, 0, TRUE);
  i += read_integer(pd, i, fd, tree, hf_ldap_message_search_sizeLimit, 0, FALSE);
  i += read_integer(pd, i, fd, tree, hf_ldap_message_search_timeLimit, 0, FALSE);
  i += read_integer(pd, i, fd, tree, hf_ldap_message_search_typesOnly, 0, TRUE);
  return i - offset;
}

static int dissect_ldap_response_search(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
  int i = offset;
  
  i += dissect_ldap_result(pd, i, fd, tree);
  
  return i - offset;
}

void
dissect_ldap(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
  proto_tree *ldap_tree = 0, *ti, *msg_tree;
  long messageLength;
  long messageId;
  long messageType;
  int i = offset;

  if (tree) 
  {
    ti = proto_tree_add_item(tree, proto_ldap, offset, END_OF_FRAME, NULL);
    ldap_tree = proto_item_add_subtree(ti, ett_ldap);
  }

  if (pd[i] != LBER_SEQUENCE)
  {
    if (tree)
      proto_tree_add_text(tree, i, 1, "Invalid LDAP packet");
    return;
  }

  i++;
  i += read_length(pd, i, fd, ldap_tree, hf_ldap_length, &messageLength);
  if (messageLength > (pi.captured_len - offset))
  {
    if (tree)
      proto_tree_add_text(tree, i, END_OF_FRAME, "Sequence length: %li, LDAP packet data length = %i\n",
			  messageLength, pi.captured_len - offset);
    return;
  }
  
  i += read_integer(pd, i, fd, ldap_tree, hf_ldap_message_id, &messageId, FALSE);
  messageType = pd[i++];
  
  if (check_col(fd, COL_PROTOCOL))
    col_add_str(fd, COL_PROTOCOL, "LDAP");

  if (check_col(fd, COL_INFO))
    col_add_fstr(fd, COL_INFO, "MsgId=%li MsgType=%s",
		 messageId, message_type_str(messageType));

  if (tree) 
  {
    ti = proto_tree_add_item(ldap_tree, hf_ldap_message_type, i - 1, 1, messageType);
    msg_tree = proto_item_add_subtree(ti, ett_ldap_message);

    switch (messageType)
    {
     case LDAP_REQ_BIND:
      dissect_ldap_request_bind(pd, i, fd, msg_tree);
      break;
     case LDAP_REQ_SEARCH:
      dissect_ldap_request_search(pd, i, fd, msg_tree);
      break;
     case LDAP_RES_BIND:
      dissect_ldap_response_bind(pd, i, fd, msg_tree);
      break;
     case LDAP_RES_SEARCH_RESULT:
      dissect_ldap_response_search(pd, i, fd, msg_tree);
      break;
    }
  }
}

void
proto_register_ldap(void)
{
  static value_string result_codes[] = {
    {0, "Success"},
    {1, "Operations error"},
    {2, "Protocol error"},
    {3, "Time limit exceeded"},
    {4, "Size limit exceeded"},
    {5, "Compare false"},
    {6, "Compare true"},
    {7, "Authentication method not supported"},
    {8, "Strong authentication required"},
    {10, "Referral"},
    {11, "Administrative limit exceeded"},
    {12, "Unavailable critical extension"},
    {13, "Confidentiality required"},
    {14, "SASL bind in progress"},
    {16, "No such attribute"},
    {17, "Undefined attribute type"},
    {18, "Inappropriate matching"},
    {19, "Constraint violation"},
    {20, "Attribute or value exists"},
    {21, "Invalid attribute syntax"},
    {32, "No such object"},
    {33, "Alias problem"},
    {34, "Invalid DN syntax"},
    {36, "Alias derefetencing problem"},
    {48, "Inappropriate authentication"},
    {49, "Invalid credentials"},
    {50, "Insufficient access rights"},
    {51, "Busy"},
    {52, "Unavailable"},
    {53, "Unwilling to perform"},
    {54, "Loop detected"},
    {64, "Naming violation"},
    {65, "Objectclass violation"},
    {66, "Not allowed on non-leaf"},
    {67, "Not allowed on RDN"},
    {68, "Entry already exists"},
    {69, "Objectclass modification prohibited"},
    {71, "Affects multiple DSAs"},
    {80, "Other"},
  };

  static value_string auth_types[] = {
    {LDAP_AUTH_NONE, "None"},
    {LDAP_AUTH_SIMPLE, "Simple"},
    {LDAP_AUTH_SIMPLE_30, "Simple"},
    {LDAP_AUTH_KRBV4, "Kerberos"},
    {LDAP_AUTH_KRBV41, "Kerberos V4.1"},
    {LDAP_AUTH_KRBV41_30, "Kerberos V4.1"},
    {LDAP_AUTH_KRBV42, "Kerberos V4.2"},
    {LDAP_AUTH_KRBV42_30, "Kerberos V4.2"},
  };
  
  static value_string search_scope[] = {
    {0x00, "Base"},
    {0x01, "Single"},
    {0x02, "Subtree"},
  };
    
  static value_string search_dereference[] = {
    {0x00, "Never"},
    {0x01, "Searching"},
    {0x02, "Base Object"},
    {0x03, "Always"},
  };
  
  static hf_register_info hf[] = {
    { &hf_ldap_length,
      { "Length",		"ldap.length",
	FT_INT32, BASE_DEC, NULL, 0x0,
	"LDAP Length" }},
	  
    { &hf_ldap_message_id,
      { "Message Id",		"ldap.message_id",
	FT_INT32, BASE_DEC, NULL, 0x0,
	"LDAP Message Id" }},
    { &hf_ldap_message_type,
      { "Message Type",		"ldap.message_type",
	FT_UINT8, BASE_HEX, &msgTypes, 0x0,
	"LDAP Message Type" }},
    { &hf_ldap_message_length,
      { "Message Length",		"ldap.message_length",
	FT_INT32, BASE_DEC, NULL, 0x0,
	"LDAP Message Length" }},

    { &hf_ldap_message_result,
      { "Result Code",		"ldap.result.code",
	FT_INT8, BASE_HEX, result_codes, 0x0,
	"LDAP Result Code" }},
    { &hf_ldap_message_result_matcheddn,
      { "Matched DN",		"ldap.result.matcheddn",
	FT_STRING, BASE_NONE, NULL, 0x0,
	"LDAP Result Matched DN" }},
    { &hf_ldap_message_result_errormsg,
      { "Error Message",		"ldap.result.errormsg",
	FT_STRING, BASE_NONE, NULL, 0x0,
	"LDAP Result Error Message" }},
    { &hf_ldap_message_result_referral,
      { "Referral",		"ldap.result.referral",
	FT_STRING, BASE_NONE, NULL, 0x0,
	"LDAP Result Referral URL" }},

    { &hf_ldap_message_bind_version,
      { "Version",		"ldap.bind.version",
	FT_INT32, BASE_DEC, NULL, 0x0,
	"LDAP Bind Version" }},
    { &hf_ldap_message_bind_dn,
      { "DN",			"ldap.bind.dn",
	FT_STRING, BASE_NONE, NULL, 0x0,
	"LDAP Bind Distinguished Name" }},
    { &hf_ldap_message_bind_auth,
      { "Auth Type",		"ldap.bind.auth_type",
	FT_INT8, BASE_HEX, auth_types, 0x0,
	"LDAP Bind Auth Type" }},
    { &hf_ldap_message_bind_auth_password,
      { "Password",		"ldap.bind.password",
	FT_STRING, BASE_NONE, NULL, 0x0,
	"LDAP Bind Password" }},

    { &hf_ldap_message_search_base,
      { "Base DN",		"ldap.search.basedn",
	FT_STRING, BASE_NONE, NULL, 0x0,
	"LDAP Search Base Distinguished Name" }},
    { &hf_ldap_message_search_scope,
      { "Scope",			"ldap.search.scope",
	FT_UINT8, BASE_HEX, search_scope, 0x0,
	"LDAP Search Scope" }},
    { &hf_ldap_message_search_deref,
      { "Dereference",		"ldap.search.dereference",
	FT_UINT8, BASE_HEX, search_dereference, 0x0,
	"LDAP Search Dereference" }},
    { &hf_ldap_message_search_sizeLimit,
      { "Size Limit",		"ldap.search.sizelimit",
	FT_INT32, BASE_DEC, NULL, 0x0,
	"LDAP Search Size Limit" }},
    { &hf_ldap_message_search_timeLimit,
      { "Time Limit",		"ldap.search.timelimit",
	FT_INT32, BASE_DEC, NULL, 0x0,
	"LDAP Search Time Limit" }},
    { &hf_ldap_message_search_typesOnly,
      { "Attributes Only",	"ldap.search.typesonly",
	FT_BOOLEAN, BASE_NONE, NULL, 0x0,
	"LDAP Search Attributes Only" }},
  };

  static gint *ett[] = {
    &ett_ldap,
    &ett_ldap_message,
    &ett_ldap_referrals,
  };

  proto_ldap = proto_register_protocol("Lightweight Directory Access Protocol", "ldap");
  proto_register_field_array(proto_ldap, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));
}
/* packet-ldap.h
 *
 * $Id: packet-ldap.h,v 1.1 2000/02/15 21:02:32 gram Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxx>
 * 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.
 */

#define LBER_BOOLEAN            0x01L
#define LBER_INTEGER            0x02L
#define LBER_BITSTRING          0x03L
#define LBER_OCTETSTRING        0x04L
#define LBER_NULL               0x05L
#define LBER_ENUMERATED         0x0aL
#define LBER_SEQUENCE           0x30L   /* constructed */
#define LBER_SET                0x31L   /* constructed */
#define OLD_LBER_SEQUENCE       0x10L   /* w/o constructed bit - broken */
#define OLD_LBER_SET            0x11L   /* w/o constructed bit - broken */

#define LDAP_REQ_BIND                   0x60L   /* application + constructed */
#define LDAP_REQ_UNBIND                 0x42L   /* application + primitive   */
#define LDAP_REQ_SEARCH                 0x63L   /* application + constructed */
#define LDAP_REQ_MODIFY                 0x66L   /* application + constructed */
#define LDAP_REQ_ADD                    0x68L   /* application + constructed */
#define LDAP_REQ_DELETE                 0x4aL   /* application + primitive   */
#define LDAP_REQ_MODRDN                 0x6cL   /* application + constructed */
#define LDAP_REQ_COMPARE                0x6eL   /* application + constructed */
#define LDAP_REQ_ABANDON                0x50L   /* application + primitive   */

#define LDAP_REQ_UNBIND_30              0x62L
#define LDAP_REQ_DELETE_30              0x6aL
#define LDAP_REQ_ABANDON_30             0x70L

#define OLD_LDAP_REQ_BIND               0x00L
#define OLD_LDAP_REQ_UNBIND             0x02L
#define OLD_LDAP_REQ_SEARCH             0x03L
#define OLD_LDAP_REQ_MODIFY             0x06L
#define OLD_LDAP_REQ_ADD                0x08L
#define OLD_LDAP_REQ_DELETE             0x0aL
#define OLD_LDAP_REQ_MODRDN             0x0cL
#define OLD_LDAP_REQ_COMPARE            0x0eL
#define OLD_LDAP_REQ_ABANDON            0x10L

#define LDAP_RES_BIND                   0x61L   /* application + constructed */
#define LDAP_RES_SEARCH_ENTRY           0x64L   /* application + constructed */
#define LDAP_RES_SEARCH_RESULT          0x65L   /* application + constructed */
#define LDAP_RES_MODIFY                 0x67L   /* application + constructed */
#define LDAP_RES_ADD                    0x69L   /* application + constructed */
#define LDAP_RES_DELETE                 0x6bL   /* application + constructed */
#define LDAP_RES_MODRDN                 0x6dL   /* application + constructed */
#define LDAP_RES_COMPARE                0x6fL   /* application + constructed */

#define OLD_LDAP_RES_BIND               0x01L
#define OLD_LDAP_RES_SEARCH_ENTRY       0x04L
#define OLD_LDAP_RES_SEARCH_RESULT      0x05L
#define OLD_LDAP_RES_MODIFY             0x07L
#define OLD_LDAP_RES_ADD                0x09L
#define OLD_LDAP_RES_DELETE             0x0bL
#define OLD_LDAP_RES_MODRDN             0x0dL
#define OLD_LDAP_RES_COMPARE            0x0fL

#define LDAP_AUTH_NONE          0x00L   /* no authentication              */
#define LDAP_AUTH_SIMPLE        0x80L   /* context specific + primitive   */
#define LDAP_AUTH_KRBV4         0xffL   /* means do both of the following */
#define LDAP_AUTH_KRBV41        0x81L   /* context specific + primitive   */
#define LDAP_AUTH_KRBV42        0x82L   /* context specific + primitive   */

#define LDAP_AUTH_SIMPLE_30     0xa0L   /* context specific + constructed */
#define LDAP_AUTH_KRBV41_30     0xa1L   /* context specific + constructed */
#define LDAP_AUTH_KRBV42_30     0xa2L   /* context specific + constructed */

#define OLD_LDAP_AUTH_SIMPLE    0x00L
#define OLD_LDAP_AUTH_KRBV4     0x01L
#define OLD_LDAP_AUTH_KRBV42    0x02L




void dissect_ldap(const u_char *, int, frame_data *, proto_tree *);