Don't try to reassemble short frames - but do still pass them through at
[obnox/wireshark/wip.git] / packet-ldap.c
1 /* packet-ldap.c
2  * Routines for ldap packet dissection
3  *
4  * $Id: packet-ldap.c,v 1.41 2002/03/31 22:17:37 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /*
26  * This is not a complete implementation. It doesn't handle the full version 3, more specifically,
27  * it handles only the commands of version 2, but any additional characteristics of the ver3 command are supported.
28  * It's also missing extensible search filters.
29  * 
30  * There should probably be alot more error checking, I simply assume that if we have a full packet, it will be a complete
31  * and correct packet.
32  * 
33  * AFAIK, it will handle all messages used by the OpenLDAP 1.2.9 server and libraries which was my goal. I do plan to add
34  * the remaining commands as time permits but this is not a priority to me. Send me an email if you need it and I'll see what
35  * I can do.
36  * 
37  * Doug Nazar
38  * nazard@dragoninc.on.ca
39  */
40
41 #ifdef HAVE_CONFIG_H
42 # include "config.h"
43 #endif
44
45 #include <stdio.h>
46
47 #ifdef HAVE_SYS_TYPES_H
48 # include <sys/types.h>
49 #endif
50
51 #ifdef HAVE_NETINET_IN_H
52 # include <netinet/in.h>
53 #endif
54
55 #include <string.h>
56 #include <glib.h>
57
58 #ifdef NEED_SNPRINTF_H
59 # include "snprintf.h"
60 #endif
61
62 #include <epan/packet.h>
63
64 #include "packet-ldap.h"
65 #include "asn1.h"
66 #include "prefs.h"
67
68 static int proto_ldap = -1;
69 static int hf_ldap_length = -1;
70 static int hf_ldap_message_id = -1;
71 static int hf_ldap_message_type = -1;
72 static int hf_ldap_message_length = -1;
73
74 static int hf_ldap_message_result = -1;
75 static int hf_ldap_message_result_matcheddn = -1;
76 static int hf_ldap_message_result_errormsg = -1;
77 static int hf_ldap_message_result_referral = -1;
78
79 static int hf_ldap_message_bind_version = -1;
80 static int hf_ldap_message_bind_dn = -1;
81 static int hf_ldap_message_bind_auth = -1;
82 static int hf_ldap_message_bind_auth_password = -1;
83
84 static int hf_ldap_message_search_base = -1;
85 static int hf_ldap_message_search_scope = -1;
86 static int hf_ldap_message_search_deref = -1;
87 static int hf_ldap_message_search_sizeLimit = -1;
88 static int hf_ldap_message_search_timeLimit = -1;
89 static int hf_ldap_message_search_typesOnly = -1;
90 static int hf_ldap_message_search_filter = -1;
91
92 static int hf_ldap_message_dn = -1;
93 static int hf_ldap_message_attribute = -1;
94 static int hf_ldap_message_value = -1;
95
96 static int hf_ldap_message_modrdn_name = -1;
97 static int hf_ldap_message_modrdn_delete = -1;
98 static int hf_ldap_message_modrdn_superior = -1;
99
100 static int hf_ldap_message_compare = -1;
101
102 static int hf_ldap_message_modify_add = -1;
103 static int hf_ldap_message_modify_replace = -1;
104 static int hf_ldap_message_modify_delete = -1;
105
106 static int hf_ldap_message_abandon_msgid = -1;
107
108 static gint ett_ldap = -1;
109 static gint ett_ldap_message = -1;
110 static gint ett_ldap_referrals = -1;
111 static gint ett_ldap_attribute = -1;
112
113 /* desegmentation of LDAP */
114 static gboolean ldap_desegment = TRUE;
115
116 #define TCP_PORT_LDAP                   389
117
118 static value_string msgTypes [] = {
119   {LDAP_REQ_BIND, "Bind Request"},
120   {LDAP_REQ_UNBIND, "Unbind Request"},
121   {LDAP_REQ_SEARCH, "Search Request"},
122   {LDAP_REQ_MODIFY, "Modify Request"},
123   {LDAP_REQ_ADD, "Add Request"},
124   {LDAP_REQ_DELETE, "Delete Request"},
125   {LDAP_REQ_MODRDN, "Modify RDN Request"},
126   {LDAP_REQ_COMPARE, "Compare Request"},
127   {LDAP_REQ_ABANDON, "Abandon Request"},
128   {LDAP_REQ_EXTENDED, "Extended Request"},
129     
130   {LDAP_RES_BIND, "Bind Result"},
131   {LDAP_RES_SEARCH_ENTRY, "Search Entry"},
132   {LDAP_RES_SEARCH_RESULT, "Search Result"},
133   {LDAP_RES_SEARCH_REF, "Search Result Reference"},
134   {LDAP_RES_MODIFY, "Modify Result"},
135   {LDAP_RES_ADD, "Add Result"},
136   {LDAP_RES_DELETE, "Delete Result"},
137   {LDAP_RES_MODRDN, "Modify RDN Result"},
138   {LDAP_RES_COMPARE, "Compare Result"},
139   {LDAP_REQ_EXTENDED, "Extended Response"},
140   {0, NULL},
141 };
142
143 static int read_length(ASN1_SCK *a, proto_tree *tree, int hf_id, guint *len)
144 {
145   guint length = 0;
146   gboolean def = FALSE;
147   int start = a->offset;
148   int ret;
149   
150   ret = asn1_length_decode(a, &def, &length);
151   if (ret != ASN1_ERR_NOERROR) {
152     if (tree) {
153       proto_tree_add_text(tree, a->tvb, start, 0,
154         "%s: ERROR: Couldn't parse length: %s",
155         proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
156     }
157     return ret;
158   }
159
160   if (len)
161     *len = length;
162
163   if (tree)
164     proto_tree_add_uint(tree, hf_id, a->tvb, start, a->offset-start, length);
165
166   return ASN1_ERR_NOERROR;
167 }
168
169 static int read_sequence(ASN1_SCK *a, guint *len)
170 {
171   guint cls, con, tag;
172   gboolean def;
173   guint length;
174   int ret;
175   
176   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
177   if (ret != ASN1_ERR_NOERROR)
178     return ret;
179   if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
180     return ASN1_ERR_WRONG_TYPE;
181
182   if (len)
183     *len = length;
184   
185   return ASN1_ERR_NOERROR;
186 }
187
188 static int read_set(ASN1_SCK *a, guint *len)
189 {
190   guint cls, con, tag;
191   gboolean def;
192   guint length;
193   int ret;
194   
195   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
196   if (ret != ASN1_ERR_NOERROR)
197     return ret;
198   if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SET)
199     return ASN1_ERR_WRONG_TYPE;
200   
201   if (len)
202     *len = length;
203   
204   return ASN1_ERR_NOERROR;
205 }
206
207 static int read_integer_value(ASN1_SCK *a, proto_tree *tree, int hf_id,
208         proto_item **new_item, guint *i, int start, guint length)
209 {
210   guint integer = 0;
211   proto_item *temp_item = NULL;
212   int ret;
213
214   ret = asn1_uint32_value_decode(a, length, &integer);
215   if (ret != ASN1_ERR_NOERROR) {
216     if (tree) {
217       proto_tree_add_text(tree, a->tvb, start, 0,
218        "%s: ERROR: Couldn't parse value: %s",
219         proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
220     }
221     return ret;
222   }
223
224   if (i)
225     *i = integer;
226
227   if (tree)
228     temp_item = proto_tree_add_uint(tree, hf_id, a->tvb, start, a->offset-start, integer);
229
230   if (new_item)
231     *new_item = temp_item;
232
233   return ASN1_ERR_NOERROR;
234 }
235
236 static int read_integer(ASN1_SCK *a, proto_tree *tree, int hf_id,
237         proto_item **new_item, guint *i, guint expected_tag)
238 {
239   guint cls, con, tag;
240   gboolean def;
241   guint length;
242   int start = a->offset;
243   int ret;
244   
245   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
246   if (ret == ASN1_ERR_NOERROR) {
247     if (cls != ASN1_UNI || con != ASN1_PRI || tag != expected_tag)
248       ret = ASN1_ERR_WRONG_TYPE;
249   }
250   if (ret != ASN1_ERR_NOERROR) {
251     if (tree) {
252       proto_tree_add_text(tree, a->tvb, start, 0,
253         "%s: ERROR: Couldn't parse header: %s",
254         (hf_id != -1) ? proto_registrar_get_name(hf_id) : "LDAP message",
255         asn1_err_to_str(ret));
256     }
257     return ret;
258   }
259
260   return read_integer_value(a, tree, hf_id, new_item, i, start, length);
261 }
262
263 static int read_boolean_value(ASN1_SCK *a, proto_tree *tree, int hf_id,
264         proto_item **new_item, guint *i, int start, guint length)
265 {
266   guint integer = 0;
267   proto_item *temp_item = NULL;
268   int ret;
269
270   ret = asn1_uint32_value_decode(a, length, &integer);
271   if (ret != ASN1_ERR_NOERROR) {
272     if (tree) {
273       proto_tree_add_text(tree, a->tvb, start, 0,
274         "%s: ERROR: Couldn't parse value: %s",
275         proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
276     }
277     return ret;
278   }
279
280   if (i)
281     *i = integer;
282
283   if (tree)
284     temp_item = proto_tree_add_boolean(tree, hf_id, a->tvb, start, a->offset-start, integer);
285   if (new_item)
286     *new_item = temp_item;
287
288   return ASN1_ERR_NOERROR;
289 }
290
291 static int read_boolean(ASN1_SCK *a, proto_tree *tree, int hf_id,
292         proto_item **new_item, guint *i)
293 {
294   guint cls, con, tag;
295   gboolean def;
296   guint length;
297   int start = a->offset;
298   int ret;
299   
300   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
301   if (ret == ASN1_ERR_NOERROR) {
302     if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_BOL)
303       ret = ASN1_ERR_WRONG_TYPE;
304   }
305   if (ret != ASN1_ERR_NOERROR) {
306     if (tree) {
307       proto_tree_add_text(tree, a->tvb, start, 0,
308         "%s: ERROR: Couldn't parse header: %s",
309         proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
310     }
311     return ret;
312   }
313
314   return read_boolean_value(a, tree, hf_id, new_item, i, start, length);
315 }
316
317 static int read_string_value(ASN1_SCK *a, proto_tree *tree, int hf_id,
318         proto_item **new_item, char **s, int start, guint length)
319 {
320   guchar *string;
321   proto_item *temp_item = NULL;
322   int ret;
323   
324   if (length)
325   {
326     ret = asn1_string_value_decode(a, length, &string);
327     if (ret != ASN1_ERR_NOERROR) {
328       if (tree) {
329         proto_tree_add_text(tree, a->tvb, start, 0,
330           "%s: ERROR: Couldn't parse value: %s",
331           proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
332       }
333       return ret;
334     }
335     string = g_realloc(string, length + 1);
336     string[length] = '\0';
337   }
338   else
339     string = "(null)";
340     
341   if (tree)
342     temp_item = proto_tree_add_string(tree, hf_id, a->tvb, start, a->offset - start, string);
343   if (new_item)
344     *new_item = temp_item;
345
346   if (s && length)
347     *s = string;
348   else if (length)
349     g_free(string);
350
351   return ASN1_ERR_NOERROR;
352 }
353
354 static int read_string(ASN1_SCK *a, proto_tree *tree, int hf_id,
355         proto_item **new_item, char **s, guint expected_cls, guint expected_tag)
356 {
357   guint cls, con, tag;
358   gboolean def;
359   guint length;
360   int start = a->offset;
361   int ret;
362   
363   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
364   if (ret == ASN1_ERR_NOERROR) {
365     if (cls != expected_cls || con != ASN1_PRI || tag != expected_tag)
366       ret = ASN1_ERR_WRONG_TYPE;
367   }
368   if (ret != ASN1_ERR_NOERROR) {
369     if (tree) {
370       proto_tree_add_text(tree, a->tvb, start, 0,
371         "%s: ERROR: Couldn't parse header: %s",
372         proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
373     }
374     return ret;
375   }
376
377   return read_string_value(a, tree, hf_id, new_item, s, start, length);
378 }
379
380 static int parse_filter_strings(ASN1_SCK *a, char **filter, guint *filter_length, const guchar *operation)
381 {
382   guchar *string;
383   guchar *string2;
384   guint string_length;
385   guint string2_length;
386   guint string_bytes;
387   char *filterp;
388   int ret;
389
390   ret = asn1_octet_string_decode(a, &string, &string_length, &string_bytes);
391   if (ret != ASN1_ERR_NOERROR)
392     return ret;
393   ret = asn1_octet_string_decode(a, &string2, &string2_length, &string_bytes);
394   if (ret != ASN1_ERR_NOERROR)
395     return ret;
396   *filter_length += 2 + strlen(operation) + string_length + string2_length;
397   *filter = g_realloc(*filter, *filter_length);
398   filterp = *filter + strlen(*filter);
399   *filterp++ = '(';
400   if (string_length != 0) {
401         memcpy(filterp, string, string_length);
402         filterp += string_length;
403   }
404   strcpy(filterp, operation);
405   filterp += strlen(operation);
406   if (string2_length != 0) {
407         memcpy(filterp, string2, string2_length);
408         filterp += string2_length;
409   }
410   *filterp++ = ')';
411   *filterp = '\0';
412   g_free(string);
413   g_free(string2);
414   return ASN1_ERR_NOERROR;
415 }
416
417 /* Richard Dawe: To parse substring filters, I added this function. */
418 static int parse_filter_substrings(ASN1_SCK *a, char **filter, guint *filter_length)
419 {
420   int end;
421   guchar *string;
422   char *filterp;
423   guint string_length;
424   guint string_bytes;
425   guint seq_len;
426   guint header_bytes;  
427   int ret, any_valued;
428
429   /* For ASN.1 parsing of octet strings */
430   guint        cls;
431   guint        con;
432   guint        tag;
433   gboolean     def;
434
435   ret = asn1_octet_string_decode(a, &string, &string_length, &string_bytes);
436   if (ret != ASN1_ERR_NOERROR)
437     return ret;
438
439   ret = asn1_sequence_decode(a, &seq_len, &header_bytes);
440   if (ret != ASN1_ERR_NOERROR)
441     return ret;
442
443   *filter_length += 2 + 1 + string_length;
444   *filter = g_realloc(*filter, *filter_length);
445   
446   filterp = *filter + strlen(*filter);
447   *filterp++ = '(';
448   if (string_length != 0) {
449     memcpy(filterp, string, string_length);
450     filterp += string_length;
451   }
452   *filterp++ = '=';
453   *filterp = '\0';
454   g_free(string);
455
456   /* Now decode seq_len's worth of octet strings. */
457   any_valued = 0;
458   end = a->offset + seq_len;
459
460   while (a->offset < end) {
461     /* Octet strings here are context-specific, which
462      * asn1_octet_string_decode() barfs on. Emulate it, but don't barf. */
463     ret = asn1_header_decode (a, &cls, &con, &tag, &def, &string_length);
464     if (ret != ASN1_ERR_NOERROR)
465       return ret;
466
467     /* XXX - check the tag? */
468     if (cls != ASN1_CTX || con != ASN1_PRI) {
469         /* XXX - handle the constructed encoding? */
470         return ASN1_ERR_WRONG_TYPE;
471     }
472     if (!def)
473         return ASN1_ERR_LENGTH_NOT_DEFINITE;
474
475     ret = asn1_string_value_decode(a, (int) string_length, &string);
476     if (ret != ASN1_ERR_NOERROR)
477       return ret;
478
479     /* If we have an 'any' component with a string value, we need to append
480      * an extra asterisk before final component. */
481     if ((tag == 1) && (string_length != 0))
482       any_valued = 1;
483
484     if ( (tag == 1) || ((tag == 2) && any_valued) )
485       (*filter_length)++;
486     *filter_length += string_length;
487     *filter = g_realloc(*filter, *filter_length);
488
489     filterp = *filter + strlen(*filter);
490     if ( (tag == 1) || ((tag == 2) && any_valued) )
491       *filterp++ = '*';
492     if (tag == 2)
493       any_valued = 0;
494     if (string_length != 0) {
495       memcpy(filterp, string, string_length);
496       filterp += string_length;
497     }
498     *filterp = '\0';
499     g_free(string);
500   }
501
502   if (any_valued)
503   {
504     (*filter_length)++;
505     *filter = g_realloc(*filter, *filter_length);
506     filterp = *filter + strlen(*filter);
507     *filterp++ = '*';
508   }
509   
510   /* NB: Allocated byte for this earlier */
511   *filterp++ = ')';
512   *filterp = '\0';
513
514   return ASN1_ERR_NOERROR;
515 }
516
517 /* Returns -1 if we're at the end, returns an ASN1_ERR value otherwise. */
518 static int parse_filter(ASN1_SCK *a, char **filter, guint *filter_length,
519                         int *end)
520 {
521   guint cls, con, tag;
522   guint length;
523   gboolean def;
524   int ret;
525
526   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
527   if (ret != ASN1_ERR_NOERROR)
528     return ret;
529   
530   if (*end == 0)
531   {
532     *end = a->offset + length;
533     *filter_length = 1;
534     *filter = g_malloc0(*filter_length);
535   }
536
537   if (cls == ASN1_CTX)  /* XXX - handle other types as errors? */
538   {
539     switch (tag)
540     {
541      case LDAP_FILTER_AND:
542       {
543         int add_end;
544
545         if (con != ASN1_CON)
546           return ASN1_ERR_WRONG_TYPE;
547         add_end = a->offset + length;
548         *filter_length += 3;
549         *filter = g_realloc(*filter, *filter_length);
550         strcat(*filter, "(&");
551         while ((ret = parse_filter(a, filter, filter_length, &add_end))
552                 == ASN1_ERR_NOERROR)
553           continue;
554         if (ret != -1)
555           return ret;
556         strcat(*filter, ")");
557       }
558       break;
559      case LDAP_FILTER_OR:
560       {
561         int or_end;
562
563         if (con != ASN1_CON)
564           return ASN1_ERR_WRONG_TYPE;
565         or_end = a->offset + length;
566         *filter_length += 3;
567         *filter = g_realloc(*filter, *filter_length);
568         strcat(*filter, "(|");
569         while ((ret = parse_filter(a, filter, filter_length, &or_end))
570                 == ASN1_ERR_NOERROR)
571           continue;
572         if (ret != -1)
573           return ret;
574         strcat(*filter, ")");
575       }
576       break;
577      case LDAP_FILTER_NOT:
578       {
579         int not_end;
580
581         if (con != ASN1_CON)
582           return ASN1_ERR_WRONG_TYPE;
583         not_end = a->offset + length;
584         *filter_length += 3;
585         *filter = g_realloc(*filter, *filter_length);
586         strcat(*filter, "(!");
587         ret = parse_filter(a, filter, filter_length, &not_end);
588         if (ret != -1 && ret != ASN1_ERR_NOERROR)
589           return ret;
590         strcat(*filter, ")");
591       }
592       break;
593      case LDAP_FILTER_EQUALITY:
594       if (con != ASN1_CON)
595         return ASN1_ERR_WRONG_TYPE;
596       ret = parse_filter_strings(a, filter, filter_length, "=");
597       if (ret != ASN1_ERR_NOERROR)
598         return ret;
599       break;
600      case LDAP_FILTER_GE:
601       if (con != ASN1_CON)
602         return ASN1_ERR_WRONG_TYPE;
603       ret = parse_filter_strings(a, filter, filter_length, ">=");
604       if (ret != ASN1_ERR_NOERROR)
605         return ret;
606       break;
607      case LDAP_FILTER_LE:
608       if (con != ASN1_CON)
609         return ASN1_ERR_WRONG_TYPE;
610       ret = parse_filter_strings(a, filter, filter_length, "<=");
611       if (ret != -1 && ret != ASN1_ERR_NOERROR)
612         return ret;
613       break;
614      case LDAP_FILTER_APPROX:
615       if (con != ASN1_CON)
616         return ASN1_ERR_WRONG_TYPE;
617       ret = parse_filter_strings(a, filter, filter_length, "~=");
618       if (ret != ASN1_ERR_NOERROR)
619         return ret;
620       break;
621      case LDAP_FILTER_PRESENT:
622       {
623         guchar *string;
624         char *filterp;
625     
626         if (con != ASN1_PRI)
627           return ASN1_ERR_WRONG_TYPE;
628         ret = asn1_string_value_decode(a, length, &string);
629         if (ret != ASN1_ERR_NOERROR)
630           return ret;
631         *filter_length += 4 + length;
632         *filter = g_realloc(*filter, *filter_length);
633         filterp = *filter + strlen(*filter);
634         *filterp++ = '(';
635         if (length != 0) {
636           memcpy(filterp, string, length);
637           filterp += length;
638         }
639         *filterp++ = '=';
640         *filterp++ = '*';
641         *filterp++ = ')';
642         *filterp = '\0';
643         g_free(string);
644       }
645       break;
646      case LDAP_FILTER_SUBSTRINGS:
647       if (con != ASN1_CON)
648         return ASN1_ERR_WRONG_TYPE;
649       /* Richard Dawe: Handle substrings */
650       ret = parse_filter_substrings(a, filter, filter_length);
651       if (ret != ASN1_ERR_NOERROR)
652         return ret;
653       break;
654      default:
655       return ASN1_ERR_WRONG_TYPE;
656     }
657   }
658   
659   if (a->offset == *end)
660     return -1;
661   else
662     return ret;
663 }
664
665 static gboolean read_filter(ASN1_SCK *a, proto_tree *tree, int hf_id)
666 {
667   int start = a->offset;
668   char *filter = 0;
669   guint filter_length = 0;
670   int end = 0;
671   int ret;
672      
673   while ((ret = parse_filter(a, &filter, &filter_length, &end))
674         == ASN1_ERR_NOERROR)
675     continue;
676
677   if (tree) {
678     if (ret != -1) {
679       proto_tree_add_text(tree, a->tvb, start, 0,
680         "%s: ERROR: Can't parse filter: %s",
681         proto_registrar_get_name(hf_id), asn1_err_to_str(ret));
682     } else
683       proto_tree_add_string(tree, hf_id, a->tvb, start, a->offset-start, filter);
684   }
685
686   g_free(filter);
687
688   return (ret == -1) ? TRUE : FALSE;
689 }
690
691 /********************************************************************************************/
692
693 static void dissect_ldap_result(ASN1_SCK *a, proto_tree *tree)
694 {
695   guint resultCode = 0;
696   int ret;
697   
698   if (read_integer(a, tree, hf_ldap_message_result, 0, &resultCode, ASN1_ENUM) != ASN1_ERR_NOERROR)
699     return;
700   if (read_string(a, tree, hf_ldap_message_result_matcheddn, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
701     return;
702   if (read_string(a, tree, hf_ldap_message_result_errormsg, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
703     return;
704
705   if (resultCode == 10)         /* Referral */
706   {
707     int start = a->offset;
708     int end;
709     guint length;
710     proto_item *ti;
711     proto_tree *referralTree;
712     
713     ret = read_sequence(a, &length);
714     if (ret != ASN1_ERR_NOERROR) {
715       if (tree) {
716         proto_tree_add_text(tree, a->tvb, start, 0,
717             "ERROR: Couldn't parse referral URL sequence header: %s",
718             asn1_err_to_str(ret));
719       }
720       return;
721     }
722     ti = proto_tree_add_text(tree, a->tvb, start, length, "Referral URLs");
723     referralTree = proto_item_add_subtree(ti, ett_ldap_referrals);
724
725     end = a->offset + length;
726     while (a->offset < end) {
727       if (read_string(a, referralTree, hf_ldap_message_result_referral, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
728         return;
729     }
730   }
731 }
732
733 static void dissect_ldap_request_bind(ASN1_SCK *a, proto_tree *tree)
734 {
735   guint cls, con, tag;
736   guint def, length;
737   int start;
738   int ret;
739
740   if (read_integer(a, tree, hf_ldap_message_bind_version, 0, 0, ASN1_INT) != ASN1_ERR_NOERROR)
741     return;
742   if (read_string(a, tree, hf_ldap_message_bind_dn, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
743     return;
744
745   start = a->offset;
746   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
747   if (ret == ASN1_ERR_NOERROR) {
748     if (cls != ASN1_CTX) {
749       /* RFCs 1777 and 2251 say these are context-specific types */
750       ret = ASN1_ERR_WRONG_TYPE;
751     }
752   }
753   if (ret != ASN1_ERR_NOERROR) {
754     proto_tree_add_text(tree, a->tvb, start, 0,
755       "%s: ERROR: Couldn't parse header: %s",
756       proto_registrar_get_name(hf_ldap_message_bind_auth),
757       asn1_err_to_str(ret));
758     return;
759   }
760   proto_tree_add_uint(tree, hf_ldap_message_bind_auth, a->tvb, start,
761                         a->offset - start, tag);
762   switch (tag)
763   {
764    case LDAP_AUTH_SIMPLE:
765     if (read_string_value(a, tree, hf_ldap_message_bind_auth_password, NULL,
766                           NULL, start, length) != ASN1_ERR_NOERROR)
767       return;
768     break;
769
770     /* For Kerberos V4, dissect it as a ticket. */
771     /* For SASL, dissect it as SaslCredentials. */
772   }
773 }
774
775 static void dissect_ldap_response_bind(ASN1_SCK *a, proto_tree *tree)
776 {
777   /* FIXME: handle SASL data */
778   dissect_ldap_result(a, tree);
779 }
780
781 static void dissect_ldap_request_search(ASN1_SCK *a, proto_tree *tree)
782 {
783   guint seq_length;
784   int end;
785   int ret;
786   
787   if (read_string(a, tree, hf_ldap_message_search_base, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
788     return;
789   if (read_integer(a, tree, hf_ldap_message_search_scope, 0, 0, ASN1_ENUM) != ASN1_ERR_NOERROR)
790     return;
791   if (read_integer(a, tree, hf_ldap_message_search_deref, 0, 0, ASN1_ENUM) != ASN1_ERR_NOERROR)
792     return;
793   if (read_integer(a, tree, hf_ldap_message_search_sizeLimit, 0, 0, ASN1_INT) != ASN1_ERR_NOERROR)
794     return;
795   if (read_integer(a, tree, hf_ldap_message_search_timeLimit, 0, 0, ASN1_INT) != ASN1_ERR_NOERROR)
796     return;
797   if (read_boolean(a, tree, hf_ldap_message_search_typesOnly, 0, 0) != ASN1_ERR_NOERROR)
798     return;
799   if (!read_filter(a, tree, hf_ldap_message_search_filter))
800     return;
801   ret = read_sequence(a, &seq_length);
802   if (ret != ASN1_ERR_NOERROR) {
803     if (tree) {
804       proto_tree_add_text(tree, a->tvb, a->offset, 0,
805           "ERROR: Couldn't parse LDAP attribute sequence header: %s",
806           asn1_err_to_str(ret));
807     }
808     return;
809   }
810   end = a->offset + seq_length;
811   while (a->offset < end) {
812     if (read_string(a, tree, hf_ldap_message_attribute, 0, 0, ASN1_UNI,
813                     ASN1_OTS) != ASN1_ERR_NOERROR)
814       return;
815   }
816 }
817
818 static void dissect_ldap_response_search_entry(ASN1_SCK *a, proto_tree *tree)
819 {
820   guint seq_length;
821   int end_of_sequence;
822   int ret;
823  
824   if (read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
825     return;
826   ret = read_sequence(a, &seq_length);
827   if (ret != ASN1_ERR_NOERROR) {
828     if (tree) {
829       proto_tree_add_text(tree, a->tvb, a->offset, 0,
830           "ERROR: Couldn't parse search entry response sequence header: %s",
831           asn1_err_to_str(ret));
832     }
833     return;
834   }
835
836   end_of_sequence = a->offset + seq_length;
837   while (a->offset < end_of_sequence)
838   {
839     proto_item *ti;
840     proto_tree *attr_tree;
841     guint set_length;
842     int end_of_set;
843
844     ret = read_sequence(a, 0);
845     if (ret != ASN1_ERR_NOERROR) {
846       if (tree) {
847         proto_tree_add_text(tree, a->tvb, a->offset, 0,
848             "ERROR: Couldn't parse LDAP attribute sequence header: %s",
849             asn1_err_to_str(ret));
850       }
851       return;
852     }
853     if (read_string(a, tree, hf_ldap_message_attribute, &ti, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
854       return;
855     attr_tree = proto_item_add_subtree(ti, ett_ldap_attribute);
856
857     ret = read_set(a, &set_length);
858     if (ret != ASN1_ERR_NOERROR) {
859       if (tree) {
860         proto_tree_add_text(attr_tree, a->tvb, a->offset, 0,
861             "ERROR: Couldn't parse LDAP value set header: %s",
862             asn1_err_to_str(ret));
863       }
864       return;
865     }
866     end_of_set = a->offset + set_length;
867     while (a->offset < end_of_set) {
868       if (read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI,
869                       ASN1_OTS) != ASN1_ERR_NOERROR)
870         return;
871     }
872   }
873 }
874
875 static void dissect_ldap_request_add(ASN1_SCK *a, proto_tree *tree)
876 {
877   guint seq_length;
878   int end_of_sequence;
879   int ret;
880   
881   if (read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
882     return;
883
884   ret = read_sequence(a, &seq_length);
885   if (ret != ASN1_ERR_NOERROR) {
886     if (tree) {
887       proto_tree_add_text(tree, a->tvb, a->offset, 0,
888           "ERROR: Couldn't parse add request sequence header: %s",
889           asn1_err_to_str(ret));
890     }
891     return;
892   }
893
894   end_of_sequence = a->offset + seq_length;
895   while (a->offset < end_of_sequence)
896   {
897     proto_item *ti;
898     proto_tree *attr_tree;
899     guint set_length;
900     int end_of_set;
901
902     ret = read_sequence(a, 0);
903     if (ret != ASN1_ERR_NOERROR) {
904       if (tree) {
905         proto_tree_add_text(tree, a->tvb, a->offset, 0,
906             "ERROR: Couldn't parse LDAP attribute sequence header: %s",
907             asn1_err_to_str(ret));
908       }
909       return;
910     }
911     if (read_string(a, tree, hf_ldap_message_attribute, &ti, 0, ASN1_UNI,
912                     ASN1_OTS) != ASN1_ERR_NOERROR)
913       return;
914     attr_tree = proto_item_add_subtree(ti, ett_ldap_attribute);
915
916     ret = read_set(a, &set_length);
917     if (ret != ASN1_ERR_NOERROR) {
918       if (tree) {
919         proto_tree_add_text(attr_tree, a->tvb, a->offset, 0,
920             "ERROR: Couldn't parse LDAP value set header: %s",
921             asn1_err_to_str(ret));
922       }
923       return;
924     }
925     end_of_set = a->offset + set_length;
926     while (a->offset < end_of_set) {
927       if (read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
928         return;
929     }
930   }
931 }
932
933 static void dissect_ldap_request_delete(ASN1_SCK *a, proto_tree *tree,
934                 int start, guint length)
935 {
936   read_string_value(a, tree, hf_ldap_message_dn, NULL, NULL, start, length);
937 }
938
939 static void dissect_ldap_request_modifyrdn(ASN1_SCK *a, proto_tree *tree,
940                 guint length)
941 {
942   int start = a->offset;
943
944   if (read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
945     return;
946   if (read_string(a, tree, hf_ldap_message_modrdn_name, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
947     return;
948   if (read_boolean(a, tree, hf_ldap_message_modrdn_delete, 0, 0) != ASN1_ERR_NOERROR)
949     return;
950   
951   if (a->offset < (int) (start + length)) {
952     /* LDAP V3 Modify DN operation, with newSuperior */
953     if (read_string(a, tree, hf_ldap_message_modrdn_superior, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
954       return;
955   }
956 }
957
958 static void dissect_ldap_request_compare(ASN1_SCK *a, proto_tree *tree)
959 {
960   int start;
961   int length;
962   char *string1 = 0;
963   char *string2 = 0;
964   char *compare;
965   int ret;
966   
967   if (read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
968     return;
969   ret = read_sequence(a, 0);
970   if (ret != ASN1_ERR_NOERROR) {
971     if (tree) {
972       proto_tree_add_text(tree, a->tvb, a->offset, 0,
973           "ERROR: Couldn't parse compare request sequence header: %s",
974           asn1_err_to_str(ret));
975     }
976     return;
977   }
978
979   start = a->offset;
980   ret = read_string(a, 0, -1, 0, &string1, ASN1_UNI, ASN1_OTS);
981   if (ret != ASN1_ERR_NOERROR) {
982     if (tree) {
983       proto_tree_add_text(tree, a->tvb, start, 0,
984         "ERROR: Couldn't parse compare type: %s", asn1_err_to_str(ret));
985     }
986     return;
987   }
988   ret = read_string(a, 0, -1, 0, &string2, ASN1_UNI, ASN1_OTS);
989   if (ret != ASN1_ERR_NOERROR) {
990     if (tree) {
991       proto_tree_add_text(tree, a->tvb, start, 0,
992         "ERROR: Couldn't parse compare value: %s", asn1_err_to_str(ret));
993     }
994     return;
995   }
996
997   length = 2 + strlen(string1) + strlen(string2);
998   compare = g_malloc0(length);
999   snprintf(compare, length, "%s=%s", string1, string2);
1000   proto_tree_add_string(tree, hf_ldap_message_compare, a->tvb, start,
1001       a->offset-start, compare);
1002   
1003   g_free(string1);
1004   g_free(string2);
1005   g_free(compare);
1006   
1007   return;
1008 }
1009
1010 static void dissect_ldap_request_modify(ASN1_SCK *a, proto_tree *tree)
1011 {
1012   guint seq_length;
1013   int end_of_sequence;
1014   int ret;
1015   
1016   if (read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
1017     return;
1018   ret = read_sequence(a, &seq_length);
1019   if (ret != ASN1_ERR_NOERROR) {
1020     if (tree) {
1021       proto_tree_add_text(tree, a->tvb, a->offset, 0,
1022           "ERROR: Couldn't parse modify request sequence header: %s",
1023           asn1_err_to_str(ret));
1024     }
1025     return;
1026   }
1027   end_of_sequence = a->offset + seq_length;
1028   while (a->offset < end_of_sequence)
1029   {
1030     proto_item *ti;
1031     proto_tree *attr_tree;
1032     guint set_length;
1033     int end_of_set;
1034     guint operation;
1035
1036     ret = read_sequence(a, 0);
1037     if (ret != ASN1_ERR_NOERROR) {
1038       if (tree) {
1039         proto_tree_add_text(tree, a->tvb, a->offset, 0,
1040             "ERROR: Couldn't parse modify request item sequence header: %s",
1041             asn1_err_to_str(ret));
1042       }
1043       return;
1044     }
1045     ret = read_integer(a, 0, -1, 0, &operation, ASN1_ENUM);
1046     if (ret != ASN1_ERR_NOERROR) {
1047       if (tree) {
1048         proto_tree_add_text(tree, a->tvb, a->offset, 0,
1049           "ERROR: Couldn't parse modify operation: %s",
1050           asn1_err_to_str(ret));
1051         return;
1052       }
1053     }
1054     ret = read_sequence(a, 0);
1055     if (ret != ASN1_ERR_NOERROR) {
1056       if (tree) {
1057         proto_tree_add_text(tree, a->tvb, a->offset, 0,
1058             "ERROR: Couldn't parse modify request operation sequence header: %s",
1059             asn1_err_to_str(ret));
1060       }
1061       return;
1062     }
1063
1064     switch (operation)
1065     {
1066      case LDAP_MOD_ADD:
1067       if (read_string(a, tree, hf_ldap_message_modify_add, &ti, 0, ASN1_UNI,
1068                       ASN1_OTS) != ASN1_ERR_NOERROR)
1069         return;
1070       break;
1071
1072      case LDAP_MOD_REPLACE:
1073       if (read_string(a, tree, hf_ldap_message_modify_replace, &ti, 0,
1074                       ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
1075         return;
1076       break;
1077
1078      case LDAP_MOD_DELETE:
1079       if (read_string(a, tree, hf_ldap_message_modify_delete, &ti, 0,
1080                       ASN1_UNI, ASN1_OTS) != ASN1_ERR_NOERROR)
1081         return;
1082       break;
1083
1084      default:
1085        proto_tree_add_text(tree, a->tvb, a->offset, 0,
1086             "Unknown LDAP modify operation (%u)", operation);
1087        return;
1088     }
1089     attr_tree = proto_item_add_subtree(ti, ett_ldap_attribute);
1090
1091     ret = read_set(a, &set_length);
1092     if (ret != ASN1_ERR_NOERROR) {
1093       if (tree) {
1094         proto_tree_add_text(attr_tree, a->tvb, a->offset, 0,
1095             "ERROR: Couldn't parse LDAP value set header: %s",
1096             asn1_err_to_str(ret));
1097       }
1098       return;
1099     }
1100     end_of_set = a->offset + set_length;
1101     while (a->offset < end_of_set) {
1102       if (read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI,
1103                       ASN1_OTS) != ASN1_ERR_NOERROR)
1104         return;
1105     }
1106   }
1107 }
1108
1109 static void dissect_ldap_request_abandon(ASN1_SCK *a, proto_tree *tree,
1110                 int start, guint length)
1111 {
1112   read_integer_value(a, tree, hf_ldap_message_abandon_msgid, NULL, NULL,
1113                             start, length); 
1114 }
1115
1116 static void
1117 dissect_ldap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1118 {
1119   proto_tree *ldap_tree = 0, *ti, *msg_tree;
1120   guint messageLength;
1121   guint messageId;
1122   int next_offset;
1123   guint protocolOpCls, protocolOpCon, protocolOpTag;
1124   gchar *typestr;
1125   guint opLen;
1126   ASN1_SCK a;
1127   int start;
1128   gboolean first_time = TRUE;
1129   int ret;
1130
1131   asn1_open(&a, tvb, 0);
1132
1133   while (tvb_reported_length_remaining(tvb, a.offset) > 0)
1134   {
1135     int message_id_start;
1136     int message_id_length;
1137     int message_start;
1138     
1139     /*
1140      * XXX - should handle the initial sequence specifier split across
1141      * segment boundaries.
1142      */
1143     message_start = a.offset;
1144     ret = read_sequence(&a, &messageLength);
1145     if (ret != ASN1_ERR_NOERROR)
1146     {
1147       if (first_time)
1148       {
1149         if (check_col(pinfo->cinfo, COL_PROTOCOL))
1150           col_set_str(pinfo->cinfo, COL_PROTOCOL, "LDAP");
1151         if (check_col(pinfo->cinfo, COL_INFO))
1152         {
1153           col_add_fstr(pinfo->cinfo, COL_INFO,
1154                       "Invalid LDAP message (Can't parse sequence header: %s)",
1155                       asn1_err_to_str(ret));
1156         }
1157       }
1158       if (tree)
1159       {
1160         ti = proto_tree_add_item(tree, proto_ldap, tvb, message_start, -1,
1161                                  FALSE);
1162         ldap_tree = proto_item_add_subtree(ti, ett_ldap);
1163         proto_tree_add_text(ldap_tree, tvb, message_start, -1,
1164                             "Invalid LDAP message (Can't parse sequence header: %s)",
1165                             asn1_err_to_str(ret));
1166       }
1167       break;
1168     }
1169
1170     /*
1171      * Desegmentation check.
1172      */
1173     if (ldap_desegment) {
1174         if (pinfo->can_desegment
1175             && messageLength > (guint)tvb_length_remaining(tvb, a.offset)) {
1176             /*
1177              * This frame doesn't have all of the data for this message,
1178              * but we can do reassembly on it.
1179              *
1180              * Tell the TCP dissector where the data for this message
1181              * starts in the data it handed us, and how many more bytes
1182              * we need, and return.
1183              */
1184             pinfo->desegment_offset = message_start;
1185             pinfo->desegment_len = messageLength -
1186                 tvb_length_remaining(tvb, a.offset);
1187             return;
1188         }
1189     }
1190     next_offset = a.offset + messageLength;
1191
1192     if (first_time)
1193     {
1194       if (check_col(pinfo->cinfo, COL_PROTOCOL))
1195         col_set_str(pinfo->cinfo, COL_PROTOCOL, "LDAP");
1196       if (check_col(pinfo->cinfo, COL_INFO))
1197         col_clear(pinfo->cinfo, COL_INFO);
1198     }
1199
1200     if (tree) 
1201     {
1202       ti = proto_tree_add_item(tree, proto_ldap, tvb, message_start,
1203                                next_offset - message_start, FALSE);
1204       ldap_tree = proto_item_add_subtree(ti, ett_ldap);
1205     }
1206
1207     message_id_start = a.offset;
1208     ret = read_integer(&a, 0, hf_ldap_message_id, 0, &messageId, ASN1_INT);
1209     if (ret != ASN1_ERR_NOERROR)
1210     {
1211       if (first_time && check_col(pinfo->cinfo, COL_INFO))
1212         col_add_fstr(pinfo->cinfo, COL_INFO, "Invalid LDAP packet (Can't parse Message ID: %s)",
1213                     asn1_err_to_str(ret));
1214       if (ldap_tree)
1215         proto_tree_add_text(ldap_tree, tvb, message_id_start, 1,
1216                             "Invalid LDAP packet (Can't parse Message ID: %s)",
1217                             asn1_err_to_str(ret));
1218       break;
1219     }
1220     message_id_length = a.offset - message_id_start;
1221
1222     start = a.offset;
1223     asn1_id_decode(&a, &protocolOpCls, &protocolOpCon, &protocolOpTag);
1224     if (protocolOpCls != ASN1_APL)
1225       typestr = "Bad message type (not Application)";
1226     else
1227       typestr = val_to_str(protocolOpTag, msgTypes, "Unknown message type (%u)");
1228
1229     if (first_time)
1230     {
1231       if (check_col(pinfo->cinfo, COL_INFO))
1232         col_add_fstr(pinfo->cinfo, COL_INFO, "MsgId=%u MsgType=%s",
1233                      messageId, typestr);
1234       first_time = FALSE;
1235     }
1236
1237     if (ldap_tree) 
1238     {
1239       proto_tree_add_uint_hidden(ldap_tree, hf_ldap_message_id, tvb, message_id_start, message_id_length, messageId);
1240       proto_tree_add_uint_hidden(ldap_tree, hf_ldap_message_type, tvb,
1241                                  start, a.offset - start, protocolOpTag);
1242       ti = proto_tree_add_text(ldap_tree, tvb, message_id_start, messageLength, "Message: Id=%u  %s", messageId, typestr);
1243       msg_tree = proto_item_add_subtree(ti, ett_ldap_message);
1244       start = a.offset;
1245       if (read_length(&a, msg_tree, hf_ldap_message_length, &opLen) != ASN1_ERR_NOERROR)
1246         return;
1247
1248       if (protocolOpCls != ASN1_APL)
1249       {
1250         proto_tree_add_text(msg_tree, a.tvb, a.offset, opLen,
1251                             "%s", typestr);
1252       }
1253       else
1254       {
1255         switch (protocolOpTag)
1256         {
1257          case LDAP_REQ_BIND:
1258           dissect_ldap_request_bind(&a, msg_tree);
1259           break;
1260          case LDAP_REQ_UNBIND:
1261           /* Nothing to dissect */
1262           break;
1263          case LDAP_REQ_SEARCH:
1264           dissect_ldap_request_search(&a, msg_tree);
1265           break;
1266          case LDAP_REQ_MODIFY:
1267           dissect_ldap_request_modify(&a, msg_tree);
1268           break;
1269          case LDAP_REQ_ADD:
1270           dissect_ldap_request_add(&a, msg_tree);
1271           break;
1272          case LDAP_REQ_DELETE:
1273           dissect_ldap_request_delete(&a, msg_tree, start, opLen);
1274           break;
1275          case LDAP_REQ_MODRDN:
1276           dissect_ldap_request_modifyrdn(&a, msg_tree, opLen);
1277           break;
1278          case LDAP_REQ_COMPARE:
1279           dissect_ldap_request_compare(&a, msg_tree);
1280           break;
1281          case LDAP_REQ_ABANDON:
1282           dissect_ldap_request_abandon(&a, msg_tree, start, opLen);
1283           break;
1284          case LDAP_RES_BIND:
1285           dissect_ldap_response_bind(&a, msg_tree);
1286           break;
1287          case LDAP_RES_SEARCH_ENTRY:
1288           dissect_ldap_response_search_entry(&a, msg_tree);
1289           break;
1290          case LDAP_RES_SEARCH_RESULT:
1291          case LDAP_RES_MODIFY:
1292          case LDAP_RES_ADD:
1293          case LDAP_RES_DELETE:
1294          case LDAP_RES_MODRDN:
1295          case LDAP_RES_COMPARE:
1296           dissect_ldap_result(&a, msg_tree);
1297           break;
1298          default:
1299           proto_tree_add_text(msg_tree, a.tvb, a.offset, opLen,
1300                               "Unknown LDAP operation (%u)", protocolOpTag);
1301           break;
1302         }
1303       }
1304     }
1305
1306     /*
1307      * XXX - what if "a.offset" is past the offset of the next top-level
1308      * sequence?  Show that as an error?
1309      */
1310     a.offset = next_offset;
1311   }
1312 }
1313
1314 void
1315 proto_register_ldap(void)
1316 {
1317   static value_string result_codes[] = {
1318     {0, "Success"},
1319     {1, "Operations error"},
1320     {2, "Protocol error"},
1321     {3, "Time limit exceeded"},
1322     {4, "Size limit exceeded"},
1323     {5, "Compare false"},
1324     {6, "Compare true"},
1325     {7, "Authentication method not supported"},
1326     {8, "Strong authentication required"},
1327     {10, "Referral"},
1328     {11, "Administrative limit exceeded"},
1329     {12, "Unavailable critical extension"},
1330     {13, "Confidentiality required"},
1331     {14, "SASL bind in progress"},
1332     {16, "No such attribute"},
1333     {17, "Undefined attribute type"},
1334     {18, "Inappropriate matching"},
1335     {19, "Constraint violation"},
1336     {20, "Attribute or value exists"},
1337     {21, "Invalid attribute syntax"},
1338     {32, "No such object"},
1339     {33, "Alias problem"},
1340     {34, "Invalid DN syntax"},
1341     {36, "Alias derefetencing problem"},
1342     {48, "Inappropriate authentication"},
1343     {49, "Invalid credentials"},
1344     {50, "Insufficient access rights"},
1345     {51, "Busy"},
1346     {52, "Unavailable"},
1347     {53, "Unwilling to perform"},
1348     {54, "Loop detected"},
1349     {64, "Naming violation"},
1350     {65, "Objectclass violation"},
1351     {66, "Not allowed on non-leaf"},
1352     {67, "Not allowed on RDN"},
1353     {68, "Entry already exists"},
1354     {69, "Objectclass modification prohibited"},
1355     {71, "Affects multiple DSAs"},
1356     {80, "Other"},
1357     {0,  NULL},
1358   };
1359
1360   static value_string auth_types[] = {
1361     {LDAP_AUTH_SIMPLE,    "Simple"},
1362     {LDAP_AUTH_KRBV4LDAP, "Kerberos V4 to the LDAP server"},
1363     {LDAP_AUTH_KRBV4DSA,  "Kerberos V4 to the DSA"},
1364     {LDAP_AUTH_SASL,      "SASL"},
1365     {0, NULL},
1366   };
1367   
1368   static value_string search_scope[] = {
1369     {0x00, "Base"},
1370     {0x01, "Single"},
1371     {0x02, "Subtree"},
1372     {0x00, NULL},
1373   };
1374     
1375   static value_string search_dereference[] = {
1376     {0x00, "Never"},
1377     {0x01, "Searching"},
1378     {0x02, "Base Object"},
1379     {0x03, "Always"},
1380     {0x00, NULL},
1381   };
1382   
1383   static hf_register_info hf[] = {
1384     { &hf_ldap_length,
1385       { "Length",               "ldap.length",
1386         FT_UINT32, BASE_DEC, NULL, 0x0,
1387         "LDAP Length", HFILL }},
1388           
1389     { &hf_ldap_message_id,
1390       { "Message Id",           "ldap.message_id",
1391         FT_UINT32, BASE_DEC, NULL, 0x0,
1392         "LDAP Message Id", HFILL }},
1393     { &hf_ldap_message_type,
1394       { "Message Type",         "ldap.message_type",
1395         FT_UINT8, BASE_HEX, &msgTypes, 0x0,
1396         "LDAP Message Type", HFILL }},
1397     { &hf_ldap_message_length,
1398       { "Message Length",               "ldap.message_length",
1399         FT_UINT32, BASE_DEC, NULL, 0x0,
1400         "LDAP Message Length", HFILL }},
1401
1402     { &hf_ldap_message_result,
1403       { "Result Code",          "ldap.result.code",
1404         FT_UINT8, BASE_HEX, result_codes, 0x0,
1405         "LDAP Result Code", HFILL }},
1406     { &hf_ldap_message_result_matcheddn,
1407       { "Matched DN",           "ldap.result.matcheddn",
1408         FT_STRING, BASE_NONE, NULL, 0x0,
1409         "LDAP Result Matched DN", HFILL }},
1410     { &hf_ldap_message_result_errormsg,
1411       { "Error Message",                "ldap.result.errormsg",
1412         FT_STRING, BASE_NONE, NULL, 0x0,
1413         "LDAP Result Error Message", HFILL }},
1414     { &hf_ldap_message_result_referral,
1415       { "Referral",             "ldap.result.referral",
1416         FT_STRING, BASE_NONE, NULL, 0x0,
1417         "LDAP Result Referral URL", HFILL }},
1418
1419     { &hf_ldap_message_bind_version,
1420       { "Version",              "ldap.bind.version",
1421         FT_UINT32, BASE_DEC, NULL, 0x0,
1422         "LDAP Bind Version", HFILL }},
1423     { &hf_ldap_message_bind_dn,
1424       { "DN",                   "ldap.bind.dn",
1425         FT_STRING, BASE_NONE, NULL, 0x0,
1426         "LDAP Bind Distinguished Name", HFILL }},
1427     { &hf_ldap_message_bind_auth,
1428       { "Auth Type",            "ldap.bind.auth_type",
1429         FT_UINT8, BASE_HEX, auth_types, 0x0,
1430         "LDAP Bind Auth Type", HFILL }},
1431     { &hf_ldap_message_bind_auth_password,
1432       { "Password",             "ldap.bind.password",
1433         FT_STRING, BASE_NONE, NULL, 0x0,
1434         "LDAP Bind Password", HFILL }},
1435
1436     { &hf_ldap_message_search_base,
1437       { "Base DN",              "ldap.search.basedn",
1438         FT_STRING, BASE_NONE, NULL, 0x0,
1439         "LDAP Search Base Distinguished Name", HFILL }},
1440     { &hf_ldap_message_search_scope,
1441       { "Scope",                        "ldap.search.scope",
1442         FT_UINT8, BASE_HEX, search_scope, 0x0,
1443         "LDAP Search Scope", HFILL }},
1444     { &hf_ldap_message_search_deref,
1445       { "Dereference",          "ldap.search.dereference",
1446         FT_UINT8, BASE_HEX, search_dereference, 0x0,
1447         "LDAP Search Dereference", HFILL }},
1448     { &hf_ldap_message_search_sizeLimit,
1449       { "Size Limit",           "ldap.search.sizelimit",
1450         FT_UINT32, BASE_DEC, NULL, 0x0,
1451         "LDAP Search Size Limit", HFILL }},
1452     { &hf_ldap_message_search_timeLimit,
1453       { "Time Limit",           "ldap.search.timelimit",
1454         FT_UINT32, BASE_DEC, NULL, 0x0,
1455         "LDAP Search Time Limit", HFILL }},
1456     { &hf_ldap_message_search_typesOnly,
1457       { "Attributes Only",      "ldap.search.typesonly",
1458         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1459         "LDAP Search Attributes Only", HFILL }},
1460     { &hf_ldap_message_search_filter,
1461       { "Filter",               "ldap.search.filter",
1462         FT_STRING, BASE_NONE, NULL, 0x0,
1463         "LDAP Search Filter", HFILL }},
1464     { &hf_ldap_message_dn,
1465       { "Distinguished Name",   "ldap.dn",
1466         FT_STRING, BASE_NONE, NULL, 0x0,
1467         "LDAP Distinguished Name", HFILL }},
1468     { &hf_ldap_message_attribute,
1469       { "Attribute",            "ldap.attribute",
1470         FT_STRING, BASE_NONE, NULL, 0x0,
1471         "LDAP Attribute", HFILL }},
1472     { &hf_ldap_message_value,
1473       { "Value",                "ldap.value",
1474         FT_STRING, BASE_NONE, NULL, 0x0,
1475         "LDAP Value", HFILL }},
1476
1477     { &hf_ldap_message_modrdn_name,
1478       { "New Name",             "ldap.modrdn.name",
1479         FT_STRING, BASE_NONE, NULL, 0x0,
1480         "LDAP New Name", HFILL }},
1481     { &hf_ldap_message_modrdn_delete,
1482       { "Delete Values",        "ldap.modrdn.delete",
1483         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1484         "LDAP Modify RDN - Delete original values", HFILL }},
1485     { &hf_ldap_message_modrdn_superior,
1486       { "New Location",         "ldap.modrdn.superior",
1487         FT_STRING, BASE_NONE, NULL, 0x0,
1488         "LDAP Modify RDN - New Location", HFILL }},
1489
1490     { &hf_ldap_message_compare,
1491       { "Test",         "ldap.compare.test",
1492         FT_STRING, BASE_NONE, NULL, 0x0,
1493         "LDAP Compare Test", HFILL }},
1494
1495     { &hf_ldap_message_modify_add,
1496       { "Add",                  "ldap.modify.add",
1497         FT_STRING, BASE_NONE, NULL, 0x0,
1498         "LDAP Add", HFILL }},
1499     { &hf_ldap_message_modify_replace,
1500       { "Replace",              "ldap.modify.replace",
1501         FT_STRING, BASE_NONE, NULL, 0x0,
1502         "LDAP Replace", HFILL }},
1503     { &hf_ldap_message_modify_delete,
1504       { "Delete",               "ldap.modify.delete",
1505         FT_STRING, BASE_NONE, NULL, 0x0,
1506         "LDAP Delete", HFILL }},
1507
1508     { &hf_ldap_message_abandon_msgid,
1509       { "Abandon Msg Id",       "ldap.abandon.msgid",
1510         FT_UINT32, BASE_DEC, NULL, 0x0,
1511         "LDAP Abandon Msg Id", HFILL }},
1512   };
1513
1514   static gint *ett[] = {
1515     &ett_ldap,
1516     &ett_ldap_message,
1517     &ett_ldap_referrals,
1518     &ett_ldap_attribute
1519   };
1520   module_t *ldap_module;
1521
1522   proto_ldap = proto_register_protocol("Lightweight Directory Access Protocol",
1523                                        "LDAP", "ldap");
1524   proto_register_field_array(proto_ldap, hf, array_length(hf));
1525   proto_register_subtree_array(ett, array_length(ett));
1526
1527   ldap_module = prefs_register_protocol(proto_ldap, NULL);
1528   prefs_register_bool_preference(ldap_module, "desegment_ldap_messages",
1529     "Desegment all LDAP messages spanning multiple TCP segments",
1530     "Whether the LDAP dissector should desegment all messages spanning multiple TCP segments",
1531     &ldap_desegment);
1532 }
1533
1534 void
1535 proto_reg_handoff_ldap(void)
1536 {
1537   dissector_handle_t ldap_handle;
1538
1539   ldap_handle = create_dissector_handle(dissect_ldap, proto_ldap);
1540   dissector_add("tcp.port", TCP_PORT_LDAP, ldap_handle);
1541 }