From Ronnie Sahlberg:
[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.34 2002/01/24 09:20:49 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   
149   asn1_length_decode(a, &def, &length);
150
151   if (len)
152     *len = length;
153
154   if (tree)
155     proto_tree_add_uint(tree, hf_id, a->tvb, start, a->offset-start, length);
156
157   return 0;
158 }
159
160 static int read_sequence(ASN1_SCK *a, guint *len)
161 {
162   guint cls, con, tag;
163   gboolean def;
164   guint length;
165   
166   if (asn1_header_decode(a, &cls, &con, &tag, &def, &length) != ASN1_ERR_NOERROR)
167     return 1;
168   if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
169     return 1;
170
171   if (len)
172     *len = length;
173   
174   return 0;
175 }
176
177 static int read_set(ASN1_SCK *a, guint *len)
178 {
179   guint cls, con, tag;
180   gboolean def;
181   guint length;
182   
183   if (asn1_header_decode(a, &cls, &con, &tag, &def, &length) != ASN1_ERR_NOERROR)
184     return 1;
185   if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SET)
186     return 1;
187   
188   if (len)
189     *len = length;
190   
191   return 0;
192 }
193
194 static int read_integer_value(ASN1_SCK *a, proto_tree *tree, int hf_id,
195         proto_tree **new_tree, guint *i, int start, guint length)
196 {
197   guint integer = 0;
198
199   asn1_uint32_value_decode(a, length, &integer);
200
201   if (i)
202     *i = integer;
203
204   if (tree)
205   {
206     proto_tree *temp_tree = 0;
207     temp_tree = proto_tree_add_uint(tree, hf_id, a->tvb, start, a->offset-start, integer);
208     if (new_tree)
209       *new_tree = temp_tree;
210   }
211
212   return 0;
213 }
214
215 static int read_integer(ASN1_SCK *a, proto_tree *tree, int hf_id,
216         proto_tree **new_tree, guint *i, guint expected_tag)
217 {
218   guint cls, con, tag;
219   gboolean def;
220   guint length;
221   int start = a->offset;
222   
223   if (asn1_header_decode(a, &cls, &con, &tag, &def, &length) != ASN1_ERR_NOERROR)
224     return 1;
225   if (cls != ASN1_UNI || con != ASN1_PRI || tag != expected_tag)
226     return 1;
227
228   return read_integer_value(a, tree, hf_id, new_tree, i, start, length);
229 }
230
231 static int read_boolean_value(ASN1_SCK *a, proto_tree *tree, int hf_id,
232         proto_tree **new_tree, guint *i, int start, guint length)
233 {
234   guint integer = 0;
235
236   asn1_uint32_value_decode(a, length, &integer);
237
238   if (i)
239     *i = integer;
240
241   if (tree)
242   {
243     proto_tree *temp_tree = 0;
244     temp_tree = proto_tree_add_boolean(tree, hf_id, a->tvb, start, a->offset-start, integer);
245     if (new_tree)
246       *new_tree = temp_tree;
247   }
248
249   return 0;
250 }
251
252 static int read_boolean(ASN1_SCK *a, proto_tree *tree, int hf_id,
253         proto_tree **new_tree, guint *i)
254 {
255   guint cls, con, tag;
256   gboolean def;
257   guint length;
258   int start = a->offset;
259   
260   if (asn1_header_decode(a, &cls, &con, &tag, &def, &length) != ASN1_ERR_NOERROR)
261     return 1;
262   if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_BOL)
263     return 1;
264
265   return read_boolean_value(a, tree, hf_id, new_tree, i, start, length);
266 }
267
268 static void read_string_value(ASN1_SCK *a, proto_tree *tree, int hf_id,
269         proto_tree **new_tree, char **s, int start, guint length)
270 {
271   guchar *string;
272   
273   if (length)
274   {
275     asn1_string_value_decode(a, length, &string);
276     string = g_realloc(string, length + 1);
277     string[length] = '\0';
278   }
279   else
280     string = "(null)";
281     
282   if (tree)
283   {
284     proto_tree *temp_tree;
285     temp_tree = proto_tree_add_string(tree, hf_id, a->tvb, start, a->offset - start, string);
286     if (new_tree)
287       *new_tree = temp_tree;
288   }
289
290   if (s && length)
291     *s = string;
292   else if (length)
293     g_free(string);
294 }
295
296 static int read_string(ASN1_SCK *a, proto_tree *tree, int hf_id,
297         proto_tree **new_tree, char **s, guint expected_cls, guint expected_tag)
298 {
299   guint cls, con, tag;
300   gboolean def;
301   guint length;
302   int start = a->offset;
303   int ret;
304   
305   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
306   if (ret != ASN1_ERR_NOERROR)
307     return ret;
308   if (cls != expected_cls || con != ASN1_PRI || tag != expected_tag)
309     return ASN1_ERR_WRONG_TYPE;
310
311   read_string_value(a, tree, hf_id, new_tree, s, start, length);
312   return ASN1_ERR_NOERROR;
313 }
314
315 static int parse_filter_strings(ASN1_SCK *a, char **filter, guint *filter_length, const guchar *operation)
316 {
317   guchar *string;
318   guchar *string2;
319   guint string_length;
320   guint string2_length;
321   guint string_bytes;
322   char *filterp;
323   int ret;
324
325   ret = asn1_octet_string_decode(a, &string, &string_length, &string_bytes);
326   if (ret != ASN1_ERR_NOERROR)
327     return ret;
328   ret = asn1_octet_string_decode(a, &string2, &string2_length, &string_bytes);
329   if (ret != ASN1_ERR_NOERROR)
330     return ret;
331   *filter_length += 2 + strlen(operation) + string_length + string2_length;
332   *filter = g_realloc(*filter, *filter_length);
333   filterp = *filter + strlen(*filter);
334   *filterp++ = '(';
335   if (string_length != 0) {
336         memcpy(filterp, string, string_length);
337         filterp += string_length;
338   }
339   strcpy(filterp, operation);
340   filterp += strlen(operation);
341   if (string2_length != 0) {
342         memcpy(filterp, string2, string2_length);
343         filterp += string2_length;
344   }
345   *filterp++ = ')';
346   *filterp = '\0';
347   g_free(string);
348   g_free(string2);
349   return ASN1_ERR_NOERROR;
350 }
351
352 /* Richard Dawe: To parse substring filters, I added this function. */
353 static int parse_filter_substrings(ASN1_SCK *a, char **filter, guint *filter_length)
354 {
355   int end;
356   guchar *string;
357   char *filterp;
358   guint string_length;
359   guint string_bytes;
360   guint seq_len;
361   guint header_bytes;  
362   int ret, any_valued;
363
364   /* For ASN.1 parsing of octet strings */
365   guint        cls;
366   guint        con;
367   guint        tag;
368   gboolean     def;
369
370   ret = asn1_octet_string_decode(a, &string, &string_length, &string_bytes);
371   if (ret != ASN1_ERR_NOERROR)
372     return ret;
373
374   ret = asn1_sequence_decode(a, &seq_len, &header_bytes);
375   if (ret != ASN1_ERR_NOERROR)
376     return ret;
377
378   *filter_length += 2 + 1 + string_length;
379   *filter = g_realloc(*filter, *filter_length);
380   
381   filterp = *filter + strlen(*filter);
382   *filterp++ = '(';
383   if (string_length != 0) {
384     memcpy(filterp, string, string_length);
385     filterp += string_length;
386   }
387   *filterp++ = '=';
388   *filterp = '\0';
389   g_free(string);
390
391   /* Now decode seq_len's worth of octet strings. */
392   any_valued = 0;
393   end = a->offset + seq_len;
394
395   while (a->offset < end) {
396     /* Octet strings here are context-specific, which
397      * asn1_octet_string_decode() barfs on. Emulate it, but don't barf. */
398     ret = asn1_header_decode (a, &cls, &con, &tag, &def, &string_length);
399     if (ret != ASN1_ERR_NOERROR)
400       return ret;
401
402     /* XXX - check the tag? */
403     if (cls != ASN1_CTX || con != ASN1_PRI) {
404         /* XXX - handle the constructed encoding? */
405         return ASN1_ERR_WRONG_TYPE;
406     }
407     if (!def)
408         return ASN1_ERR_LENGTH_NOT_DEFINITE;
409
410     ret = asn1_string_value_decode(a, (int) string_length, &string);
411     if (ret != ASN1_ERR_NOERROR)
412       return ret;
413
414     /* If we have an 'any' component with a string value, we need to append
415      * an extra asterisk before final component. */
416     if ((tag == 1) && (string_length != 0))
417       any_valued = 1;
418
419     if ( (tag == 1) || ((tag == 2) && any_valued) )
420       (*filter_length)++;
421     *filter_length += string_length;
422     *filter = g_realloc(*filter, *filter_length);
423
424     filterp = *filter + strlen(*filter);
425     if ( (tag == 1) || ((tag == 2) && any_valued) )
426       *filterp++ = '*';
427     if (tag == 2)
428       any_valued = 0;
429     if (string_length != 0) {
430       memcpy(filterp, string, string_length);
431       filterp += string_length;
432     }
433     *filterp = '\0';
434     g_free(string);
435   }
436
437   if (any_valued)
438   {
439     (*filter_length)++;
440     *filter = g_realloc(*filter, *filter_length);
441     filterp = *filter + strlen(*filter);
442     *filterp++ = '*';
443   }
444   
445   /* NB: Allocated byte for this earlier */
446   *filterp++ = ')';
447   *filterp = '\0';
448
449   return ASN1_ERR_NOERROR;
450 }
451
452 /* Returns -1 if we're at the end, returns an ASN1_ERR value otherwise. */
453 static int parse_filter(ASN1_SCK *a, char **filter, guint *filter_length,
454                         int *end)
455 {
456   guint cls, con, tag;
457   guint length;
458   gboolean def;
459   int ret;
460
461   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
462   if (ret != ASN1_ERR_NOERROR)
463     return ret;
464   
465   if (*end == 0)
466   {
467     *end = a->offset + length;
468     *filter_length = 1;
469     *filter = g_malloc0(*filter_length);
470   }
471
472   if (cls == ASN1_CTX)  /* XXX - handle other types as errors? */
473   {
474     switch (tag)
475     {
476      case LDAP_FILTER_AND:
477       {
478         int add_end;
479
480         if (con != ASN1_CON)
481           return ASN1_ERR_WRONG_TYPE;
482         add_end = a->offset + length;
483         *filter_length += 3;
484         *filter = g_realloc(*filter, *filter_length);
485         strcat(*filter, "(&");
486         while ((ret = parse_filter(a, filter, filter_length, &add_end))
487                 == ASN1_ERR_NOERROR)
488           continue;
489         if (ret != -1)
490           return ret;
491         strcat(*filter, ")");
492       }
493       break;
494      case LDAP_FILTER_OR:
495       {
496         int or_end;
497
498         if (con != ASN1_CON)
499           return ASN1_ERR_WRONG_TYPE;
500         or_end = a->offset + length;
501         *filter_length += 3;
502         *filter = g_realloc(*filter, *filter_length);
503         strcat(*filter, "(|");
504         while ((ret = parse_filter(a, filter, filter_length, &or_end))
505                 == ASN1_ERR_NOERROR)
506           continue;
507         if (ret != -1)
508           return ret;
509         strcat(*filter, ")");
510       }
511       break;
512      case LDAP_FILTER_NOT:
513       {
514         int not_end;
515
516         if (con != ASN1_CON)
517           return ASN1_ERR_WRONG_TYPE;
518         not_end = a->offset + length;
519         *filter_length += 3;
520         *filter = g_realloc(*filter, *filter_length);
521         strcat(*filter, "(!");
522         ret = parse_filter(a, filter, filter_length, &not_end);
523         if (ret != -1 && ret != ASN1_ERR_NOERROR)
524           return ret;
525         strcat(*filter, ")");
526       }
527       break;
528      case LDAP_FILTER_EQUALITY:
529       if (con != ASN1_CON)
530         return ASN1_ERR_WRONG_TYPE;
531       ret = parse_filter_strings(a, filter, filter_length, "=");
532       if (ret != -1 && ret != ASN1_ERR_NOERROR)
533         return ret;
534       break;
535      case LDAP_FILTER_GE:
536       if (con != ASN1_CON)
537         return ASN1_ERR_WRONG_TYPE;
538       ret = parse_filter_strings(a, filter, filter_length, ">=");
539       if (ret != -1 && ret != ASN1_ERR_NOERROR)
540         return ret;
541       break;
542      case LDAP_FILTER_LE:
543       if (con != ASN1_CON)
544         return ASN1_ERR_WRONG_TYPE;
545       ret = parse_filter_strings(a, filter, filter_length, "<=");
546       if (ret != -1 && ret != ASN1_ERR_NOERROR)
547         return ret;
548       break;
549      case LDAP_FILTER_APPROX:
550       if (con != ASN1_CON)
551         return ASN1_ERR_WRONG_TYPE;
552       ret = parse_filter_strings(a, filter, filter_length, "~=");
553       if (ret != -1 && ret != ASN1_ERR_NOERROR)
554         return ret;
555       break;
556      case LDAP_FILTER_PRESENT:
557       {
558         guchar *string;
559         char *filterp;
560     
561         if (con != ASN1_PRI)
562           return ASN1_ERR_WRONG_TYPE;
563         ret = asn1_string_value_decode(a, length, &string);
564         if (ret != ASN1_ERR_NOERROR)
565           return ret;
566         *filter_length += 4 + length;
567         *filter = g_realloc(*filter, *filter_length);
568         filterp = *filter + strlen(*filter);
569         *filterp++ = '(';
570         if (length != 0) {
571           memcpy(filterp, string, length);
572           filterp += length;
573         }
574         *filterp++ = '=';
575         *filterp++ = '*';
576         *filterp++ = ')';
577         *filterp = '\0';
578         g_free(string);
579       }
580       break;
581      case LDAP_FILTER_SUBSTRINGS:
582       if (con != ASN1_CON)
583         return ASN1_ERR_WRONG_TYPE;
584       /* Richard Dawe: Handle substrings */
585       ret = parse_filter_substrings(a, filter, filter_length);
586       if (ret != -1 && ret != ASN1_ERR_NOERROR)
587         return ret;
588       break;
589      default:
590       return ASN1_ERR_WRONG_TYPE;
591     }
592   }
593   
594   if (a->offset == *end)
595     return -1;
596   else
597     return ret;
598 }
599
600 static int read_filter(ASN1_SCK *a, proto_tree *tree, int hf_id)
601 {
602   int start = a->offset;
603   char *filter = 0;
604   guint filter_length = 0;
605   int end = 0;
606   int ret;
607      
608   while ((ret = parse_filter(a, &filter, &filter_length, &end))
609         == ASN1_ERR_NOERROR)
610     continue;
611
612   if (tree) {
613     if (ret != -1) {
614       proto_tree_add_text(tree, a->tvb, start, 0,
615         "Error parsing filter (%d)", ret);
616     } else
617       proto_tree_add_string(tree, hf_id, a->tvb, start, a->offset-start, filter);
618   }
619
620   g_free(filter);
621
622   return 0;
623 }
624
625 /********************************************************************************************/
626
627 static int dissect_ldap_result(ASN1_SCK *a, proto_tree *tree)
628 {
629   guint resultCode = 0;
630   
631   read_integer(a, tree, hf_ldap_message_result, 0, &resultCode, ASN1_ENUM);
632   read_string(a, tree, hf_ldap_message_result_matcheddn, 0, 0, ASN1_UNI, ASN1_OTS);
633   read_string(a, tree, hf_ldap_message_result_errormsg, 0, 0, ASN1_UNI, ASN1_OTS);
634
635   if (resultCode == 10)         /* Referral */
636   {
637     int start = a->offset;
638     int end;
639     guint length;
640     proto_tree *t, *referralTree;
641     
642     read_sequence(a, &length);
643     t = proto_tree_add_text(tree, a->tvb, start, length, "Referral URLs");
644     referralTree = proto_item_add_subtree(t, ett_ldap_referrals);
645
646     end = a->offset + length;
647     while (a->offset < end)
648       read_string(a, referralTree, hf_ldap_message_result_referral, 0, 0, ASN1_UNI, ASN1_OTS);
649   }
650     
651   return 0;
652 }
653
654 static int dissect_ldap_request_bind(ASN1_SCK *a, proto_tree *tree)
655 {
656   guint cls, con, tag;
657   guint def, length;
658   int start;
659
660   read_integer(a, tree, hf_ldap_message_bind_version, 0, 0, ASN1_INT);
661   read_string(a, tree, hf_ldap_message_bind_dn, 0, 0, ASN1_UNI, ASN1_OTS);
662
663   start = a->offset;
664   if (asn1_header_decode(a, &cls, &con, &tag, &def, &length) != ASN1_ERR_NOERROR)
665     return 1;   /* XXX - right return value for an error? */
666   if (cls != ASN1_CTX)
667     return 1;   /* RFCs 1777 and 2251 say these are context-specific types */
668   proto_tree_add_uint(tree, hf_ldap_message_bind_auth, a->tvb, start,
669                         a->offset - start, tag);
670   switch (tag)
671   {
672    case LDAP_AUTH_SIMPLE:
673     read_string_value(a, tree, hf_ldap_message_bind_auth_password, NULL, NULL,
674                         start, length);
675     break;
676
677     /* For Kerberos V4, dissect it as a ticket. */
678     /* For SASL, dissect it as SaslCredentials. */
679   }
680   
681   return 0;
682 }
683
684 static int dissect_ldap_response_bind(ASN1_SCK *a, proto_tree *tree)
685 {
686   dissect_ldap_result(a, tree);
687   /* FIXME: handle SASL data */
688   return 0;
689 }
690
691 static int dissect_ldap_request_search(ASN1_SCK *a, proto_tree *tree)
692 {
693   guint seq_length;
694   int end;
695   int ret;
696   
697   read_string(a, tree, hf_ldap_message_search_base, 0, 0, ASN1_UNI, ASN1_OTS);
698   read_integer(a, tree, hf_ldap_message_search_scope, 0, 0, ASN1_ENUM);
699   read_integer(a, tree, hf_ldap_message_search_deref, 0, 0, ASN1_ENUM);
700   read_integer(a, tree, hf_ldap_message_search_sizeLimit, 0, 0, ASN1_INT);
701   read_integer(a, tree, hf_ldap_message_search_timeLimit, 0, 0, ASN1_INT);
702   read_boolean(a, tree, hf_ldap_message_search_typesOnly, 0, 0);
703   ret = read_filter(a, tree, hf_ldap_message_search_filter);
704   if (ret != ASN1_ERR_NOERROR)
705     return ret;
706   read_sequence(a, &seq_length);
707   end = a->offset + seq_length;
708   while (a->offset < end) {
709     ret = read_string(a, tree, hf_ldap_message_attribute, 0, 0, ASN1_UNI, ASN1_OTS);
710     if (ret != ASN1_ERR_NOERROR)
711       return ret;
712   }
713   return ASN1_ERR_NOERROR;
714 }
715
716 static int dissect_ldap_response_search_entry(ASN1_SCK *a, proto_tree *tree)
717 {
718   guint seq_length;
719   int end_of_sequence;
720  
721   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
722   read_sequence(a, &seq_length);
723
724   end_of_sequence = a->offset + seq_length;
725   while (a->offset < end_of_sequence)
726   {
727     proto_tree *t, *attr_tree;
728     guint set_length;
729     int end_of_set;
730
731     read_sequence(a, 0);
732     read_string(a, tree, hf_ldap_message_attribute, &t, 0, ASN1_UNI, ASN1_OTS);
733     attr_tree = proto_item_add_subtree(t, ett_ldap_attribute);
734
735     read_set(a, &set_length);
736     end_of_set = a->offset + set_length;
737     while (a->offset < end_of_set)
738       read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS);
739   }
740
741   return 0;
742 }
743
744 static int dissect_ldap_request_add(ASN1_SCK *a, proto_tree *tree)
745 {
746   guint seq_length;
747   int end_of_sequence;
748   
749   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
750
751   read_sequence(a, &seq_length);
752   end_of_sequence = a->offset + seq_length;
753   while (a->offset < end_of_sequence)
754   {
755     proto_tree *t, *attr_tree;
756     guint set_length;
757     int end_of_set;
758
759     read_sequence(a, 0);
760     read_string(a, tree, hf_ldap_message_attribute, &t, 0, ASN1_UNI, ASN1_OTS);
761     attr_tree = proto_item_add_subtree(t, ett_ldap_attribute);
762
763     read_set(a, &set_length);
764     end_of_set = a->offset + set_length;
765     while (a->offset < end_of_set)
766       read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS);
767   }
768
769   return 0;
770 }
771
772 static int dissect_ldap_request_delete(ASN1_SCK *a, proto_tree *tree,
773                 int start, guint length)
774 {
775   read_string_value(a, tree, hf_ldap_message_dn, NULL, NULL, start, length);
776   return 0;
777 }
778
779 static int dissect_ldap_request_modifyrdn(ASN1_SCK *a, proto_tree *tree,
780                 guint length)
781 {
782   int start = a->offset;
783
784   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
785   read_string(a, tree, hf_ldap_message_modrdn_name, 0, 0, ASN1_UNI, ASN1_OTS);
786   read_boolean(a, tree, hf_ldap_message_modrdn_delete, 0, 0);
787   
788   if (a->offset < (int) (start + length)) {
789     /* LDAP V3 Modify DN operation, with newSuperior */
790     read_string(a, tree, hf_ldap_message_modrdn_superior, 0, 0, ASN1_UNI, ASN1_OTS);
791   }
792
793   return 0;
794 }
795
796 static int dissect_ldap_request_compare(ASN1_SCK *a, proto_tree *tree)
797 {
798   int start;
799   int length;
800   char *string1 = 0;
801   char *string2 = 0;
802   char *compare;
803   
804   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
805   read_sequence(a, 0);
806
807   start = a->offset;
808   read_string(a, 0, -1, 0, &string1, ASN1_UNI, ASN1_OTS);
809   read_string(a, 0, -1, 0, &string2, ASN1_UNI, ASN1_OTS);
810
811   if (string1 == 0 && string2 == 0) /* read_string failed */
812     return 1;
813
814   length = 2 + strlen(string1) + strlen(string2);
815   compare = g_malloc0(length);
816   snprintf(compare, length, "%s=%s", string1, string2);
817   proto_tree_add_string(tree, hf_ldap_message_compare, a->tvb, start,
818       a->offset-start, compare);
819   
820   g_free(string1);
821   g_free(string2);
822   g_free(compare);
823   
824   return 0;
825 }
826
827 static int dissect_ldap_request_modify(ASN1_SCK *a, proto_tree *tree)
828 {
829   guint seq_length;
830   int end_of_sequence;
831   
832   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
833   read_sequence(a, &seq_length);
834   end_of_sequence = a->offset + seq_length;
835   while (a->offset < end_of_sequence)
836   {
837     proto_tree *t = 0, *attr_tree;
838     guint set_length;
839     int end_of_set;
840     guint operation;
841
842     read_sequence(a, 0);
843     read_integer(a, 0, -1, 0, &operation, ASN1_ENUM);
844     read_sequence(a, 0);
845
846     switch (operation)
847     {
848      case LDAP_MOD_ADD:
849       read_string(a, tree, hf_ldap_message_modify_add, &t, 0, ASN1_UNI, ASN1_OTS);
850       break;
851      case LDAP_MOD_REPLACE:
852       read_string(a, tree, hf_ldap_message_modify_replace, &t, 0, ASN1_UNI, ASN1_OTS);
853       break;
854      case LDAP_MOD_DELETE:
855       read_string(a, tree, hf_ldap_message_modify_delete, &t, 0, ASN1_UNI, ASN1_OTS);
856       break;
857     }
858     attr_tree = proto_item_add_subtree(t, ett_ldap_attribute);
859
860     read_set(a, &set_length);
861     end_of_set = a->offset + set_length;
862     while (a->offset < end_of_set)
863       read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS);
864   }
865
866   return 0;
867 }
868
869 static int dissect_ldap_request_abandon(ASN1_SCK *a, proto_tree *tree,
870                 int start, guint length)
871 {
872   read_integer_value(a, tree, hf_ldap_message_abandon_msgid, NULL, NULL,
873                         start, length); 
874   return 0;
875 }
876
877 static void
878 dissect_ldap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
879 {
880   proto_tree *ldap_tree = 0, *ti, *msg_tree;
881   guint messageLength;
882   guint messageId;
883   int next_offset;
884   guint protocolOpCls, protocolOpCon, protocolOpTag;
885   gchar *typestr;
886   guint opLen;
887   ASN1_SCK a;
888   int start;
889   gboolean first_time = TRUE;
890   int ret;
891
892   asn1_open(&a, tvb, 0);
893
894   while (tvb_reported_length_remaining(tvb, a.offset) > 0)
895   {
896     int message_id_start;
897     int message_id_length;
898     int message_start;
899     
900     /*
901      * XXX - should handle the initial sequence specifier split across
902      * segment boundaries.
903      */
904     message_start = a.offset;
905     if (read_sequence(&a, &messageLength))
906     {
907       if (first_time)
908       {
909         if (check_col(pinfo->cinfo, COL_PROTOCOL))
910           col_set_str(pinfo->cinfo, COL_PROTOCOL, "LDAP");
911         if (check_col(pinfo->cinfo, COL_INFO))
912           col_set_str(pinfo->cinfo, COL_INFO, "Invalid LDAP packet");
913       }
914       if (tree)
915       {
916         ti = proto_tree_add_item(tree, proto_ldap, tvb, message_start, -1,
917                                  FALSE);
918         ldap_tree = proto_item_add_subtree(ti, ett_ldap);
919         proto_tree_add_text(ldap_tree, tvb, message_start, -1,
920                             "Invalid LDAP packet");
921       }
922       break;
923     }
924
925     /*
926      * Desegmentation check.
927      */
928     if (ldap_desegment) {
929         if (pinfo->can_desegment
930             && messageLength > (guint)tvb_length_remaining(tvb, a.offset)) {
931             /*
932              * This frame doesn't have all of the data for this message,
933              * but we can do reassembly on it.
934              *
935              * Tell the TCP dissector where the data for this message
936              * starts in the data it handed us, and how many more bytes
937              * we need, and return.
938              */
939             pinfo->desegment_offset = message_start;
940             pinfo->desegment_len = messageLength -
941                 tvb_length_remaining(tvb, a.offset);
942             return;
943         }
944     }
945     next_offset = a.offset + messageLength;
946
947     if (first_time)
948     {
949       if (check_col(pinfo->cinfo, COL_PROTOCOL))
950         col_set_str(pinfo->cinfo, COL_PROTOCOL, "LDAP");
951       if (check_col(pinfo->cinfo, COL_INFO))
952         col_clear(pinfo->cinfo, COL_INFO);
953     }
954
955     if (tree) 
956     {
957       ti = proto_tree_add_item(tree, proto_ldap, tvb, message_start,
958                                next_offset - message_start, FALSE);
959       ldap_tree = proto_item_add_subtree(ti, ett_ldap);
960     }
961
962     message_id_start = a.offset;
963     if (read_integer(&a, 0, -1, 0, &messageId, ASN1_INT))
964     {
965       if (first_time && check_col(pinfo->cinfo, COL_INFO))
966         col_set_str(pinfo->cinfo, COL_INFO, "Invalid LDAP packet (No Message ID)");
967       if (ldap_tree)
968         proto_tree_add_text(ldap_tree, tvb, message_id_start, 1,
969                             "Invalid LDAP packet (No Message ID)");
970       break;
971     }
972     message_id_length = a.offset - message_id_start;
973
974     start = a.offset;
975     asn1_id_decode(&a, &protocolOpCls, &protocolOpCon, &protocolOpTag);
976     if (protocolOpCls != ASN1_APL)
977       typestr = "Bad message type (not Application)";
978     else
979       typestr = val_to_str(protocolOpTag, msgTypes, "Bad message type (%u)");
980
981     if (first_time)
982     {
983       if (check_col(pinfo->cinfo, COL_INFO))
984         col_add_fstr(pinfo->cinfo, COL_INFO, "MsgId=%u MsgType=%s",
985                      messageId, typestr);
986       first_time = FALSE;
987     }
988
989     if (ldap_tree) 
990     {
991       proto_tree_add_uint_hidden(ldap_tree, hf_ldap_message_id, tvb, message_id_start, message_id_length, messageId);
992       proto_tree_add_uint_hidden(ldap_tree, hf_ldap_message_type, tvb,
993                                  start, a.offset - start, protocolOpTag);
994       ti = proto_tree_add_text(ldap_tree, tvb, message_id_start, messageLength, "Message: Id=%u  %s", messageId, typestr);
995       msg_tree = proto_item_add_subtree(ti, ett_ldap_message);
996       start = a.offset;
997       read_length(&a, msg_tree, hf_ldap_message_length, &opLen);
998
999       switch (protocolOpTag)
1000       {
1001        case LDAP_REQ_BIND:
1002         dissect_ldap_request_bind(&a, msg_tree);
1003         break;
1004        case LDAP_REQ_SEARCH:
1005         ret = dissect_ldap_request_search(&a, msg_tree);
1006         /* XXX - do something with "ret" */
1007         break;
1008        case LDAP_REQ_ADD:
1009         dissect_ldap_request_add(&a, msg_tree);
1010         break;
1011        case LDAP_REQ_DELETE:
1012         dissect_ldap_request_delete(&a, msg_tree, start, opLen);
1013         break;
1014        case LDAP_REQ_MODRDN:
1015         dissect_ldap_request_modifyrdn(&a, msg_tree, opLen);
1016         break;
1017        case LDAP_REQ_COMPARE:
1018         dissect_ldap_request_compare(&a, msg_tree);
1019         break;
1020        case LDAP_REQ_MODIFY:
1021         dissect_ldap_request_modify(&a, msg_tree);
1022         break;
1023        case LDAP_REQ_ABANDON:
1024         dissect_ldap_request_abandon(&a, msg_tree, start, opLen);
1025         break;
1026        case LDAP_RES_BIND:
1027         dissect_ldap_response_bind(&a, msg_tree);
1028         break;
1029        case LDAP_RES_SEARCH_ENTRY:
1030         dissect_ldap_response_search_entry(&a, msg_tree);
1031         break;
1032        case LDAP_RES_SEARCH_RESULT:
1033        case LDAP_RES_MODIFY:
1034        case LDAP_RES_ADD:
1035        case LDAP_RES_DELETE:
1036        case LDAP_RES_MODRDN:
1037        case LDAP_RES_COMPARE:
1038         dissect_ldap_result(&a, msg_tree);
1039         break;
1040       }
1041     }
1042
1043     /*
1044      * XXX - what if "a.offset" is past the offset of the next top-level
1045      * sequence?  Show that as an error?
1046      */
1047     a.offset = next_offset;
1048   }
1049 }
1050
1051 void
1052 proto_register_ldap(void)
1053 {
1054   static value_string result_codes[] = {
1055     {0, "Success"},
1056     {1, "Operations error"},
1057     {2, "Protocol error"},
1058     {3, "Time limit exceeded"},
1059     {4, "Size limit exceeded"},
1060     {5, "Compare false"},
1061     {6, "Compare true"},
1062     {7, "Authentication method not supported"},
1063     {8, "Strong authentication required"},
1064     {10, "Referral"},
1065     {11, "Administrative limit exceeded"},
1066     {12, "Unavailable critical extension"},
1067     {13, "Confidentiality required"},
1068     {14, "SASL bind in progress"},
1069     {16, "No such attribute"},
1070     {17, "Undefined attribute type"},
1071     {18, "Inappropriate matching"},
1072     {19, "Constraint violation"},
1073     {20, "Attribute or value exists"},
1074     {21, "Invalid attribute syntax"},
1075     {32, "No such object"},
1076     {33, "Alias problem"},
1077     {34, "Invalid DN syntax"},
1078     {36, "Alias derefetencing problem"},
1079     {48, "Inappropriate authentication"},
1080     {49, "Invalid credentials"},
1081     {50, "Insufficient access rights"},
1082     {51, "Busy"},
1083     {52, "Unavailable"},
1084     {53, "Unwilling to perform"},
1085     {54, "Loop detected"},
1086     {64, "Naming violation"},
1087     {65, "Objectclass violation"},
1088     {66, "Not allowed on non-leaf"},
1089     {67, "Not allowed on RDN"},
1090     {68, "Entry already exists"},
1091     {69, "Objectclass modification prohibited"},
1092     {71, "Affects multiple DSAs"},
1093     {80, "Other"},
1094     {0,  NULL},
1095   };
1096
1097   static value_string auth_types[] = {
1098     {LDAP_AUTH_SIMPLE,    "Simple"},
1099     {LDAP_AUTH_KRBV4LDAP, "Kerberos V4 to the LDAP server"},
1100     {LDAP_AUTH_KRBV4DSA,  "Kerberos V4 to the DSA"},
1101     {LDAP_AUTH_SASL,      "SASL"},
1102     {0, NULL},
1103   };
1104   
1105   static value_string search_scope[] = {
1106     {0x00, "Base"},
1107     {0x01, "Single"},
1108     {0x02, "Subtree"},
1109     {0x00, NULL},
1110   };
1111     
1112   static value_string search_dereference[] = {
1113     {0x00, "Never"},
1114     {0x01, "Searching"},
1115     {0x02, "Base Object"},
1116     {0x03, "Always"},
1117     {0x00, NULL},
1118   };
1119   
1120   static hf_register_info hf[] = {
1121     { &hf_ldap_length,
1122       { "Length",               "ldap.length",
1123         FT_UINT32, BASE_DEC, NULL, 0x0,
1124         "LDAP Length", HFILL }},
1125           
1126     { &hf_ldap_message_id,
1127       { "Message Id",           "ldap.message_id",
1128         FT_UINT32, BASE_DEC, NULL, 0x0,
1129         "LDAP Message Id", HFILL }},
1130     { &hf_ldap_message_type,
1131       { "Message Type",         "ldap.message_type",
1132         FT_UINT8, BASE_HEX, &msgTypes, 0x0,
1133         "LDAP Message Type", HFILL }},
1134     { &hf_ldap_message_length,
1135       { "Message Length",               "ldap.message_length",
1136         FT_UINT32, BASE_DEC, NULL, 0x0,
1137         "LDAP Message Length", HFILL }},
1138
1139     { &hf_ldap_message_result,
1140       { "Result Code",          "ldap.result.code",
1141         FT_UINT8, BASE_HEX, result_codes, 0x0,
1142         "LDAP Result Code", HFILL }},
1143     { &hf_ldap_message_result_matcheddn,
1144       { "Matched DN",           "ldap.result.matcheddn",
1145         FT_STRING, BASE_NONE, NULL, 0x0,
1146         "LDAP Result Matched DN", HFILL }},
1147     { &hf_ldap_message_result_errormsg,
1148       { "Error Message",                "ldap.result.errormsg",
1149         FT_STRING, BASE_NONE, NULL, 0x0,
1150         "LDAP Result Error Message", HFILL }},
1151     { &hf_ldap_message_result_referral,
1152       { "Referral",             "ldap.result.referral",
1153         FT_STRING, BASE_NONE, NULL, 0x0,
1154         "LDAP Result Referral URL", HFILL }},
1155
1156     { &hf_ldap_message_bind_version,
1157       { "Version",              "ldap.bind.version",
1158         FT_UINT32, BASE_DEC, NULL, 0x0,
1159         "LDAP Bind Version", HFILL }},
1160     { &hf_ldap_message_bind_dn,
1161       { "DN",                   "ldap.bind.dn",
1162         FT_STRING, BASE_NONE, NULL, 0x0,
1163         "LDAP Bind Distinguished Name", HFILL }},
1164     { &hf_ldap_message_bind_auth,
1165       { "Auth Type",            "ldap.bind.auth_type",
1166         FT_UINT8, BASE_HEX, auth_types, 0x0,
1167         "LDAP Bind Auth Type", HFILL }},
1168     { &hf_ldap_message_bind_auth_password,
1169       { "Password",             "ldap.bind.password",
1170         FT_STRING, BASE_NONE, NULL, 0x0,
1171         "LDAP Bind Password", HFILL }},
1172
1173     { &hf_ldap_message_search_base,
1174       { "Base DN",              "ldap.search.basedn",
1175         FT_STRING, BASE_NONE, NULL, 0x0,
1176         "LDAP Search Base Distinguished Name", HFILL }},
1177     { &hf_ldap_message_search_scope,
1178       { "Scope",                        "ldap.search.scope",
1179         FT_UINT8, BASE_HEX, search_scope, 0x0,
1180         "LDAP Search Scope", HFILL }},
1181     { &hf_ldap_message_search_deref,
1182       { "Dereference",          "ldap.search.dereference",
1183         FT_UINT8, BASE_HEX, search_dereference, 0x0,
1184         "LDAP Search Dereference", HFILL }},
1185     { &hf_ldap_message_search_sizeLimit,
1186       { "Size Limit",           "ldap.search.sizelimit",
1187         FT_UINT32, BASE_DEC, NULL, 0x0,
1188         "LDAP Search Size Limit", HFILL }},
1189     { &hf_ldap_message_search_timeLimit,
1190       { "Time Limit",           "ldap.search.timelimit",
1191         FT_UINT32, BASE_DEC, NULL, 0x0,
1192         "LDAP Search Time Limit", HFILL }},
1193     { &hf_ldap_message_search_typesOnly,
1194       { "Attributes Only",      "ldap.search.typesonly",
1195         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1196         "LDAP Search Attributes Only", HFILL }},
1197     { &hf_ldap_message_search_filter,
1198       { "Filter",               "ldap.search.filter",
1199         FT_STRING, BASE_NONE, NULL, 0x0,
1200         "LDAP Search Filter", HFILL }},
1201     { &hf_ldap_message_dn,
1202       { "Distinguished Name",   "ldap.dn",
1203         FT_STRING, BASE_NONE, NULL, 0x0,
1204         "LDAP Distinguished Name", HFILL }},
1205     { &hf_ldap_message_attribute,
1206       { "Attribute",            "ldap.attribute",
1207         FT_STRING, BASE_NONE, NULL, 0x0,
1208         "LDAP Attribute", HFILL }},
1209     { &hf_ldap_message_value,
1210       { "Value",                "ldap.value",
1211         FT_STRING, BASE_NONE, NULL, 0x0,
1212         "LDAP Value", HFILL }},
1213
1214     { &hf_ldap_message_modrdn_name,
1215       { "New Name",             "ldap.modrdn.name",
1216         FT_STRING, BASE_NONE, NULL, 0x0,
1217         "LDAP New Name", HFILL }},
1218     { &hf_ldap_message_modrdn_delete,
1219       { "Delete Values",        "ldap.modrdn.delete",
1220         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1221         "LDAP Modify RDN - Delete original values", HFILL }},
1222     { &hf_ldap_message_modrdn_superior,
1223       { "New Location",         "ldap.modrdn.superior",
1224         FT_STRING, BASE_NONE, NULL, 0x0,
1225         "LDAP Modify RDN - New Location", HFILL }},
1226
1227     { &hf_ldap_message_compare,
1228       { "Test",         "ldap.compare.test",
1229         FT_STRING, BASE_NONE, NULL, 0x0,
1230         "LDAP Compare Test", HFILL }},
1231
1232     { &hf_ldap_message_modify_add,
1233       { "Add",                  "ldap.modify.add",
1234         FT_STRING, BASE_NONE, NULL, 0x0,
1235         "LDAP Add", HFILL }},
1236     { &hf_ldap_message_modify_replace,
1237       { "Replace",              "ldap.modify.replace",
1238         FT_STRING, BASE_NONE, NULL, 0x0,
1239         "LDAP Replace", HFILL }},
1240     { &hf_ldap_message_modify_delete,
1241       { "Delete",               "ldap.modify.delete",
1242         FT_STRING, BASE_NONE, NULL, 0x0,
1243         "LDAP Delete", HFILL }},
1244
1245     { &hf_ldap_message_abandon_msgid,
1246       { "Abandon Msg Id",       "ldap.abandon.msgid",
1247         FT_UINT32, BASE_DEC, NULL, 0x0,
1248         "LDAP Abandon Msg Id", HFILL }},
1249   };
1250
1251   static gint *ett[] = {
1252     &ett_ldap,
1253     &ett_ldap_message,
1254     &ett_ldap_referrals,
1255     &ett_ldap_attribute
1256   };
1257   module_t *ldap_module;
1258
1259   proto_ldap = proto_register_protocol("Lightweight Directory Access Protocol",
1260                                        "LDAP", "ldap");
1261   proto_register_field_array(proto_ldap, hf, array_length(hf));
1262   proto_register_subtree_array(ett, array_length(ett));
1263
1264   ldap_module = prefs_register_protocol(proto_ldap, NULL);
1265   prefs_register_bool_preference(ldap_module, "desegment_ldap_messages",
1266     "Desegment all LDAP messages spanning multiple TCP segments",
1267     "Whether the LDAP dissector should desegment all messages spanning multiple TCP segments",
1268     &ldap_desegment);
1269 }
1270
1271 void
1272 proto_reg_handoff_ldap(void)
1273 {
1274   dissector_handle_t ldap_handle;
1275
1276   ldap_handle = create_dissector_handle(dissect_ldap, proto_ldap);
1277   dissector_add("tcp.port", TCP_PORT_LDAP, ldap_handle);
1278 }