Add a comment explaining why we're defining S_ISDIR and company.
[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.12 2000/05/31 05:07:16 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
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 # ifdef HAVE_STDARG_H
60 #  include <stdarg.h>
61 # else
62 #  include <varargs.h>
63 # endif
64 # include "snprintf.h"
65 #endif
66
67 #include "packet.h"
68
69 #include "packet-ldap.h"
70 #include "asn1.h"
71
72 static int proto_ldap = -1;
73 static int hf_ldap_length = -1;
74 static int hf_ldap_message_id = -1;
75 static int hf_ldap_message_type = -1;
76 static int hf_ldap_message_length = -1;
77
78 static int hf_ldap_message_result = -1;
79 static int hf_ldap_message_result_matcheddn = -1;
80 static int hf_ldap_message_result_errormsg = -1;
81 static int hf_ldap_message_result_referral = -1;
82
83 static int hf_ldap_message_bind_version = -1;
84 static int hf_ldap_message_bind_dn = -1;
85 static int hf_ldap_message_bind_auth = -1;
86 static int hf_ldap_message_bind_auth_password = -1;
87
88 static int hf_ldap_message_search_base = -1;
89 static int hf_ldap_message_search_scope = -1;
90 static int hf_ldap_message_search_deref = -1;
91 static int hf_ldap_message_search_sizeLimit = -1;
92 static int hf_ldap_message_search_timeLimit = -1;
93 static int hf_ldap_message_search_typesOnly = -1;
94 static int hf_ldap_message_search_filter = -1;
95
96 static int hf_ldap_message_dn = -1;
97 static int hf_ldap_message_attribute = -1;
98 static int hf_ldap_message_value = -1;
99
100 static int hf_ldap_message_modrdn_name = -1;
101 static int hf_ldap_message_modrdn_delete = -1;
102 static int hf_ldap_message_modrdn_superior = -1;
103
104 static int hf_ldap_message_compare = -1;
105
106 static int hf_ldap_message_modify_add = -1;
107 static int hf_ldap_message_modify_replace = -1;
108 static int hf_ldap_message_modify_delete = -1;
109
110 static int hf_ldap_message_abandon_msgid = -1;
111
112 static gint ett_ldap = -1;
113 static gint ett_ldap_message = -1;
114 static gint ett_ldap_referrals = -1;
115 static gint ett_ldap_attribute = -1;
116
117 #define TCP_PORT_LDAP                   389
118
119 static value_string msgTypes [] = {
120   {LDAP_REQ_BIND, "Bind Request"},
121   {LDAP_REQ_UNBIND, "Unbind Request"},
122   {LDAP_REQ_SEARCH, "Search Request"},
123   {LDAP_REQ_MODIFY, "Modify Request"},
124   {LDAP_REQ_ADD, "Add Request"},
125   {LDAP_REQ_DELETE, "Delete Request"},
126   {LDAP_REQ_MODRDN, "Modify RDN Request"},
127   {LDAP_REQ_COMPARE, "Compare Request"},
128   {LDAP_REQ_ABANDON, "Abandon Request"},
129   {LDAP_REQ_EXTENDED, "Extended Request"},
130     
131   {LDAP_RES_BIND, "Bind Result"},
132   {LDAP_RES_SEARCH_ENTRY, "Search Entry"},
133   {LDAP_RES_SEARCH_RESULT, "Search Result"},
134   {LDAP_RES_SEARCH_REF, "Search Result Reference"},
135   {LDAP_RES_MODIFY, "Modify Result"},
136   {LDAP_RES_ADD, "Add Result"},
137   {LDAP_RES_DELETE, "Delete Result"},
138   {LDAP_RES_MODRDN, "Modify RDN Result"},
139   {LDAP_RES_COMPARE, "Compare Result"},
140   {LDAP_REQ_EXTENDED, "Extended Response"},
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   const guchar *start = a->pointer;
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, NullTVB, start-a->begin, a->pointer-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, const guchar *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, NullTVB, start-a->begin, a->pointer-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   const guchar *start = a->pointer;
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, const guchar *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, NullTVB, start-a->begin, a->pointer-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   const guchar *start = a->pointer;
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, const guchar *start, guint length)
270 {
271   guchar *string;
272   
273   if (length)
274   {
275     asn1_octet_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, NullTVB, start - a->begin, a->pointer - 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   const guchar *start = a->pointer;
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   gint string_length;
320   gint string2_length;
321   guint string_bytes;
322   int ret;
323
324   ret = asn1_octet_string_decode(a, &string, &string_length, &string_bytes);
325   if (ret != ASN1_ERR_NOERROR)
326     return ret;
327   ret = asn1_octet_string_decode(a, &string2, &string2_length, &string_bytes);
328   if (ret != ASN1_ERR_NOERROR)
329     return ret;
330   *filter_length += 2 + strlen(operation) + string_length + string2_length;
331   *filter = g_realloc(*filter, *filter_length);
332   sprintf(*filter + strlen(*filter), "(%.*s%s%.*s)", string_length, string, operation, string2_length, string2);
333   g_free(string);
334   g_free(string2);
335   return ASN1_ERR_NOERROR;
336 }
337
338 /* Richard Dawe: To parse substring filters, I added this function. */
339 static int parse_filter_substrings(ASN1_SCK *a, char **filter, guint *filter_length)
340 {
341   guchar *end;
342   guchar *string = NULL;
343   gint string_length;
344   guint string_bytes;
345   guint seq_len;
346   guint header_bytes;  
347   int ret, any_valued;
348
349   /* For ASN.1 parsing of octet strings */
350   guint        cls;
351   guint        con;
352   guint        tag;
353   gboolean     def;
354
355   ret = asn1_octet_string_decode(a, &string, &string_length, &string_bytes);
356   if (ret != ASN1_ERR_NOERROR)
357     return ret;
358
359   ret = asn1_sequence_decode(a, &seq_len, &header_bytes);
360   if (ret != ASN1_ERR_NOERROR)
361     return ret;
362
363   *filter_length += 2 + 1 + strlen(string);
364   *filter = g_realloc(*filter, *filter_length);
365   sprintf(*filter + strlen(*filter), "(%.*s=", string_length, string);
366   g_free(string);
367
368   /* Now decode seq_len's worth of octet strings. */
369   any_valued = 0;
370   end = (guchar *) (a->pointer + seq_len);
371
372   while (a->pointer < end) {
373     /* Octet strings here are context-specific, which
374      * asn1_octet_string_decode() barfs on. Emulate it, but don't barf. */
375     ret = asn1_header_decode (a, &cls, &con, &tag, &def, &string_length);
376     if (ret != ASN1_ERR_NOERROR)
377       return ret;
378
379     if (cls != ASN1_CTX || con != ASN1_PRI) {
380         /* XXX - handle the constructed encoding? */
381         return ASN1_ERR_WRONG_TYPE;
382     }
383     if (!def)
384         return ASN1_ERR_LENGTH_NOT_DEFINITE;
385
386     ret = asn1_octet_string_value_decode(a, (int) string_length, &string);
387     if (ret != ASN1_ERR_NOERROR)
388       return ret;
389
390     /* If we have an 'any' component with a string value, we need to append
391      * an extra asterisk before final component. */
392     if ((tag == 1) && (string_length > 0))
393       any_valued = 1;
394
395     if ( (tag == 1) || ((tag == 2) && any_valued) )
396       (*filter_length)++;
397     *filter_length += strlen(string);
398     *filter = g_realloc(*filter, *filter_length);
399
400     if ( (tag == 1) || ((tag == 2) && any_valued) )
401       strcat(*filter, "*");
402     if (tag == 2)
403       any_valued = 0;
404     sprintf(*filter + strlen(*filter), "%.*s", string_length, string);
405     g_free(string);
406   }
407
408   if (any_valued)
409   {
410     (*filter_length)++;
411     *filter = g_realloc(*filter, *filter_length);
412     strcat(*filter, "*");
413   }
414   
415   /* NB: Allocated byte for this earlier */
416   strcat(*filter, ")");
417
418   return ASN1_ERR_NOERROR;
419 }
420
421 /* Returns -1 if we're at the end, returns an ASN1_ERR value otherwise. */
422 static int parse_filter(ASN1_SCK *a, char **filter, guint *filter_length, const guchar **end)
423 {
424   guint cls, con, tag;
425   guint length;
426   gboolean def;
427   int ret;
428
429   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
430   if (ret != ASN1_ERR_NOERROR)
431     return ret;
432   
433   if (*end == 0)
434   {
435     *end = a->pointer + length;
436     *filter_length = 1;
437     *filter = g_malloc0(*filter_length);
438   }
439
440   if (cls == ASN1_CTX)  /* XXX - handle other types as errors? */
441   {
442     switch (tag)
443     {
444      case LDAP_FILTER_AND:
445       {
446         const guchar *add_end;
447
448         if (con != ASN1_CON)
449           return ASN1_ERR_WRONG_TYPE;
450         add_end = a->pointer + length;
451         *filter_length += 3;
452         *filter = g_realloc(*filter, *filter_length);
453         strcat(*filter, "(&");
454         while ((ret = parse_filter(a, filter, filter_length, &add_end))
455                 == ASN1_ERR_NOERROR)
456           continue;
457         if (ret != -1)
458           return ret;
459         strcat(*filter, ")");
460       }
461       break;
462      case LDAP_FILTER_OR:
463       {
464         const guchar *or_end;
465
466         if (con != ASN1_CON)
467           return ASN1_ERR_WRONG_TYPE;
468         or_end = a->pointer + length;
469         *filter_length += 3;
470         *filter = g_realloc(*filter, *filter_length);
471         strcat(*filter, "(|");
472         while ((ret = parse_filter(a, filter, filter_length, &or_end))
473                 == ASN1_ERR_NOERROR)
474           continue;
475         if (ret != -1)
476           return ret;
477         strcat(*filter, ")");
478       }
479       break;
480      case LDAP_FILTER_NOT:
481       {
482         const guchar *not_end;
483
484         if (con != ASN1_CON)
485           return ASN1_ERR_WRONG_TYPE;
486         not_end = a->pointer + length;
487         *filter_length += 3;
488         *filter = g_realloc(*filter, *filter_length);
489         strcat(*filter, "(!");
490         ret = parse_filter(a, filter, filter_length, &not_end);
491         if (ret != -1 && ret != ASN1_ERR_NOERROR)
492           return ret;
493         strcat(*filter, ")");
494       }
495       break;
496      case LDAP_FILTER_EQUALITY:
497       if (con != ASN1_CON)
498         return ASN1_ERR_WRONG_TYPE;
499       ret = parse_filter_strings(a, filter, filter_length, "=");
500       if (ret != -1 && ret != ASN1_ERR_NOERROR)
501         return ret;
502       break;
503      case LDAP_FILTER_GE:
504       if (con != ASN1_CON)
505         return ASN1_ERR_WRONG_TYPE;
506       ret = parse_filter_strings(a, filter, filter_length, ">=");
507       if (ret != -1 && ret != ASN1_ERR_NOERROR)
508         return ret;
509       break;
510      case LDAP_FILTER_LE:
511       if (con != ASN1_CON)
512         return ASN1_ERR_WRONG_TYPE;
513       ret = parse_filter_strings(a, filter, filter_length, "<=");
514       if (ret != -1 && ret != ASN1_ERR_NOERROR)
515         return ret;
516       break;
517      case LDAP_FILTER_APPROX:
518       if (con != ASN1_CON)
519         return ASN1_ERR_WRONG_TYPE;
520       ret = parse_filter_strings(a, filter, filter_length, "~=");
521       if (ret != -1 && ret != ASN1_ERR_NOERROR)
522         return ret;
523       break;
524      case LDAP_FILTER_PRESENT:
525       {
526         guchar *string;
527     
528         if (con != ASN1_PRI)
529           return ASN1_ERR_WRONG_TYPE;
530         ret = asn1_octet_string_value_decode(a, length, &string);
531         if (ret != ASN1_ERR_NOERROR)
532           return ret;
533         *filter_length += 4 + length;
534         *filter = g_realloc(*filter, *filter_length);
535         sprintf(*filter + strlen(*filter), "(%.*s=*)", (int)length, string);
536         g_free(string);
537       }
538       break;
539      case LDAP_FILTER_SUBSTRINGS:
540       if (con != ASN1_CON)
541         return ASN1_ERR_WRONG_TYPE;
542       /* Richard Dawe: Handle substrings */
543       ret = parse_filter_substrings(a, filter, filter_length);
544       if (ret != -1 && ret != ASN1_ERR_NOERROR)
545         return ret;
546       break;
547      default:
548       return ASN1_ERR_WRONG_TYPE;
549     }
550   }
551   
552   if (a->pointer == *end)
553     return -1;
554   else
555     return ret;
556 }
557
558 static int read_filter(ASN1_SCK *a, proto_tree *tree, int hf_id)
559 {
560   const guchar *start = a->pointer;
561   char *filter = 0;
562   guint filter_length = 0;
563   const guchar *end = 0;
564   int ret;
565      
566   while ((ret = parse_filter(a, &filter, &filter_length, &end))
567         == ASN1_ERR_NOERROR)
568     continue;
569
570   if (tree) {
571     if (ret != -1) {
572       proto_tree_add_text(tree, NullTVB, start-a->begin, 0,
573         "Error parsing filter (%d)", ret);
574     } else
575       proto_tree_add_string(tree, hf_id, NullTVB, start-a->begin, a->pointer-start, filter);
576   }
577
578   g_free(filter);
579
580   return 0;
581 }
582
583 /********************************************************************************************/
584
585 static int dissect_ldap_result(ASN1_SCK *a, proto_tree *tree)
586 {
587   guint resultCode = 0;
588   
589   read_integer(a, tree, hf_ldap_message_result, 0, &resultCode, ASN1_ENUM);
590   read_string(a, tree, hf_ldap_message_result_matcheddn, 0, 0, ASN1_UNI, ASN1_OTS);
591   read_string(a, tree, hf_ldap_message_result_errormsg, 0, 0, ASN1_UNI, ASN1_OTS);
592
593   if (resultCode == 10)         /* Referral */
594   {
595     const guchar *start = a->pointer;
596     const guchar *end;
597     guint length;
598     proto_tree *t, *referralTree;
599     
600     read_sequence(a, &length);
601     t = proto_tree_add_text(tree, NullTVB, start-a->begin, length, "Referral URLs");
602     referralTree = proto_item_add_subtree(t, ett_ldap_referrals);
603
604     end = a->pointer + length;;
605     while (a->pointer < end)
606       read_string(a, referralTree, hf_ldap_message_result_referral, 0, 0, ASN1_UNI, ASN1_OTS);
607   }
608     
609   return 0;
610 }
611
612 static int dissect_ldap_request_bind(ASN1_SCK *a, proto_tree *tree)
613 {
614   guint cls, con, tag;
615   guint def, length;
616   const guchar *start;
617
618   read_integer(a, tree, hf_ldap_message_bind_version, 0, 0, ASN1_INT);
619   read_string(a, tree, hf_ldap_message_bind_dn, 0, 0, ASN1_UNI, ASN1_OTS);
620
621   start = a->pointer;
622   if (asn1_header_decode(a, &cls, &con, &tag, &def, &length) != ASN1_ERR_NOERROR)
623     return 1;   /* XXX - right return value for an error? */
624   if (cls != ASN1_CTX)
625     return 1;   /* RFCs 1777 and 2251 say these are context-specific types */
626   proto_tree_add_uint(tree, hf_ldap_message_bind_auth, NullTVB, start - a->begin,
627                         a->pointer - start, tag);
628   switch (tag)
629   {
630    case LDAP_AUTH_SIMPLE:
631     read_string_value(a, tree, hf_ldap_message_bind_auth_password, NULL, NULL,
632                         start, length);
633     break;
634
635     /* For Kerberos V4, dissect it as a ticket. */
636     /* For SASL, dissect it as SaslCredentials. */
637   }
638   
639   return 0;
640 }
641
642 static int dissect_ldap_response_bind(ASN1_SCK *a, proto_tree *tree)
643 {
644   dissect_ldap_result(a, tree);
645   /* FIXME: handle SASL data */
646   return 0;
647 }
648
649 static int dissect_ldap_request_search(ASN1_SCK *a, proto_tree *tree)
650 {
651   guint seq_length;
652   const guchar *end;
653   int ret;
654   
655   read_string(a, tree, hf_ldap_message_search_base, 0, 0, ASN1_UNI, ASN1_OTS);
656   read_integer(a, tree, hf_ldap_message_search_scope, 0, 0, ASN1_ENUM);
657   read_integer(a, tree, hf_ldap_message_search_deref, 0, 0, ASN1_ENUM);
658   read_integer(a, tree, hf_ldap_message_search_sizeLimit, 0, 0, ASN1_INT);
659   read_integer(a, tree, hf_ldap_message_search_timeLimit, 0, 0, ASN1_INT);
660   read_boolean(a, tree, hf_ldap_message_search_typesOnly, 0, 0);
661   ret = read_filter(a, tree, hf_ldap_message_search_filter);
662   if (ret != ASN1_ERR_NOERROR)
663     return ret;
664   read_sequence(a, &seq_length);
665   end = a->pointer + seq_length;
666   while (a->pointer < end) {
667     ret = read_string(a, tree, hf_ldap_message_attribute, 0, 0, ASN1_UNI, ASN1_OTS);
668     if (ret != ASN1_ERR_NOERROR)
669       return ret;
670   }
671   return ASN1_ERR_NOERROR;
672 }
673
674 static int dissect_ldap_response_search_entry(ASN1_SCK *a, proto_tree *tree)
675 {
676   guint seq_length;
677   const guchar *end_of_sequence;
678  
679   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
680   read_sequence(a, &seq_length);
681
682   end_of_sequence = a->pointer + seq_length;
683   while (a->pointer < end_of_sequence)
684   {
685     proto_tree *t, *attr_tree;
686     guint set_length;
687     const guchar *end_of_set;
688
689     read_sequence(a, 0);
690     read_string(a, tree, hf_ldap_message_attribute, &t, 0, ASN1_UNI, ASN1_OTS);
691     attr_tree = proto_item_add_subtree(t, ett_ldap_attribute);
692
693     read_set(a, &set_length);
694     end_of_set = a->pointer + set_length;
695     while (a->pointer < end_of_set)
696       read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS);
697   }
698
699   return 0;
700 }
701
702 static int dissect_ldap_request_add(ASN1_SCK *a, proto_tree *tree)
703 {
704   guint seq_length;
705   const guchar *end_of_sequence;
706   
707   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
708
709   read_sequence(a, &seq_length);
710   end_of_sequence = a->pointer + seq_length;
711   while (a->pointer < end_of_sequence)
712   {
713     proto_tree *t, *attr_tree;
714     guint set_length;
715     const guchar *end_of_set;
716
717     read_sequence(a, 0);
718     read_string(a, tree, hf_ldap_message_attribute, &t, 0, ASN1_UNI, ASN1_OTS);
719     attr_tree = proto_item_add_subtree(t, ett_ldap_attribute);
720
721     read_set(a, &set_length);
722     end_of_set = a->pointer + set_length;
723     while (a->pointer < end_of_set)
724       read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS);
725   }
726
727   return 0;
728 }
729
730 static int dissect_ldap_request_delete(ASN1_SCK *a, proto_tree *tree,
731                 const guchar *start, guint length)
732 {
733   read_string_value(a, tree, hf_ldap_message_dn, NULL, NULL, start, length);
734   return 0;
735 }
736
737 static int dissect_ldap_request_modifyrdn(ASN1_SCK *a, proto_tree *tree,
738                 guint length)
739 {
740   const guchar *start = a->pointer;
741
742   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
743   read_string(a, tree, hf_ldap_message_modrdn_name, 0, 0, ASN1_UNI, ASN1_OTS);
744   read_boolean(a, tree, hf_ldap_message_modrdn_delete, 0, 0);
745   
746   if (a->pointer < (start + length)) {
747     /* LDAP V3 Modify DN operation, with newSuperior */
748     read_string(a, tree, hf_ldap_message_modrdn_superior, 0, 0, ASN1_UNI, ASN1_OTS);
749   }
750
751   return 0;
752 }
753
754 static int dissect_ldap_request_compare(ASN1_SCK *a, proto_tree *tree)
755 {
756   const guchar *start;
757   int length;
758   char *string1 = 0;
759   char *string2 = 0;
760   char *compare;
761   
762   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
763   read_sequence(a, 0);
764
765   start = a->pointer;
766   read_string(a, 0, -1, 0, &string1, ASN1_UNI, ASN1_OTS);
767   read_string(a, 0, -1, 0, &string2, ASN1_UNI, ASN1_OTS);
768
769   length = 2 + strlen(string1) + strlen(string2);
770   compare = g_malloc0(length);
771   snprintf(compare, length, "%s=%s", string1, string2);
772   proto_tree_add_string(tree, hf_ldap_message_compare, NullTVB, start-a->begin, a->pointer-start, compare);
773   
774   g_free(string1);
775   g_free(string2);
776   g_free(compare);
777   
778   return 0;
779 }
780
781 static int dissect_ldap_request_modify(ASN1_SCK *a, proto_tree *tree)
782 {
783   guint seq_length;
784   const guchar *end_of_sequence;
785   
786   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
787   read_sequence(a, &seq_length);
788   end_of_sequence = a->pointer + seq_length;
789   while (a->pointer < end_of_sequence)
790   {
791     proto_tree *t = 0, *attr_tree;
792     guint set_length;
793     const guchar *end_of_set;
794     guint operation;
795
796     read_sequence(a, 0);
797     read_integer(a, 0, -1, 0, &operation, ASN1_ENUM);
798     read_sequence(a, 0);
799
800     switch (operation)
801     {
802      case LDAP_MOD_ADD:
803       read_string(a, tree, hf_ldap_message_modify_add, &t, 0, ASN1_UNI, ASN1_OTS);
804       break;
805      case LDAP_MOD_REPLACE:
806       read_string(a, tree, hf_ldap_message_modify_replace, &t, 0, ASN1_UNI, ASN1_OTS);
807       break;
808      case LDAP_MOD_DELETE:
809       read_string(a, tree, hf_ldap_message_modify_delete, &t, 0, ASN1_UNI, ASN1_OTS);
810       break;
811     }
812     attr_tree = proto_item_add_subtree(t, ett_ldap_attribute);
813
814     read_set(a, &set_length);
815     end_of_set = a->pointer + set_length;
816     while (a->pointer < end_of_set)
817       read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS);
818   }
819
820   return 0;
821 }
822
823 static int dissect_ldap_request_abandon(ASN1_SCK *a, proto_tree *tree,
824                 const guchar *start, guint length)
825 {
826   read_integer_value(a, tree, hf_ldap_message_abandon_msgid, NULL, NULL,
827                         start, length); 
828   return 0;
829 }
830
831 static void
832 dissect_ldap(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
833 {
834   proto_tree *ldap_tree = 0, *ti, *msg_tree;
835   guint messageLength;
836   guint messageId;
837   guint protocolOpCls, protocolOpCon, protocolOpTag;
838   gchar *typestr;
839   guint opLen;
840   ASN1_SCK a;
841   const guchar *start;
842   int first_time = 1;
843   int ret;
844
845   if (tree) 
846   {
847     ti = proto_tree_add_item(tree, proto_ldap, NullTVB, offset, END_OF_FRAME, FALSE);
848     ldap_tree = proto_item_add_subtree(ti, ett_ldap);
849   }
850
851   asn1_open(&a, pd, pi.captured_len);
852   a.pointer += offset;
853
854   while (a.pointer < a.end)
855   {
856     int message_id_start;
857     int message_id_length;
858     int message_start;
859     
860     message_start = a.pointer - a.begin;
861     if (read_sequence(&a, &messageLength))
862     {
863       if (ldap_tree)
864         proto_tree_add_text(ldap_tree, NullTVB, offset, 1, "Invalid LDAP packet");
865       break;
866     }
867
868     if (messageLength > (a.end - a.pointer))
869     {
870       if (ldap_tree)
871         proto_tree_add_text(ldap_tree, NullTVB, message_start, END_OF_FRAME, "Short message! (expected: %u, actual: %u)",
872                             messageLength, a.end - a.pointer);
873       break;
874     }
875   
876     message_id_start = a.pointer - a.begin;
877     read_integer(&a, 0, -1, 0, &messageId, ASN1_INT);
878     message_id_length = (a.pointer - a.begin) - message_id_start;
879
880     start = a.pointer;
881     asn1_id_decode(&a, &protocolOpCls, &protocolOpCon, &protocolOpTag);
882     if (protocolOpCls != ASN1_APL)
883       typestr = "Bad message type (not Application)";
884     else
885       typestr = val_to_str(protocolOpTag, msgTypes, "Bad message type (%u)");
886
887     if (first_time)
888     {
889       if (check_col(fd, COL_PROTOCOL))
890         col_add_str(fd, COL_PROTOCOL, "LDAP");
891
892       if (check_col(fd, COL_INFO))
893         col_add_fstr(fd, COL_INFO, "MsgId=%u MsgType=%s",
894                      messageId, typestr);
895       first_time = 0;
896       if (!tree)
897         return;
898     }
899
900     if (ldap_tree) 
901     {
902       proto_tree_add_uint_hidden(ldap_tree, hf_ldap_message_id, NullTVB, message_id_start, message_id_length, messageId);
903       proto_tree_add_uint_hidden(ldap_tree, hf_ldap_message_type, NullTVB,
904                                  start - a.begin, a.pointer - start, protocolOpTag);
905       ti = proto_tree_add_text(ldap_tree, NullTVB, message_id_start, messageLength, "Message: Id=%u  %s", messageId, typestr);
906       msg_tree = proto_item_add_subtree(ti, ett_ldap_message);
907       start = a.pointer;
908       read_length(&a, msg_tree, hf_ldap_message_length, &opLen);
909
910       switch (protocolOpTag)
911       {
912        case LDAP_REQ_BIND:
913         dissect_ldap_request_bind(&a, msg_tree);
914         break;
915        case LDAP_REQ_SEARCH:
916         ret = dissect_ldap_request_search(&a, msg_tree);
917         if (ret != ASN1_ERR_NOERROR)
918           break;
919         break;
920        case LDAP_REQ_ADD:
921         dissect_ldap_request_add(&a, msg_tree);
922         break;
923        case LDAP_REQ_DELETE:
924         dissect_ldap_request_delete(&a, msg_tree, start, opLen);
925         break;
926        case LDAP_REQ_MODRDN:
927         dissect_ldap_request_modifyrdn(&a, msg_tree, opLen);
928         break;
929        case LDAP_REQ_COMPARE:
930         dissect_ldap_request_compare(&a, msg_tree);
931         break;
932        case LDAP_REQ_MODIFY:
933         dissect_ldap_request_modify(&a, msg_tree);
934         break;
935        case LDAP_REQ_ABANDON:
936         dissect_ldap_request_abandon(&a, msg_tree, start, opLen);
937         break;
938        case LDAP_RES_BIND:
939         dissect_ldap_response_bind(&a, msg_tree);
940         break;
941        case LDAP_RES_SEARCH_ENTRY:
942         dissect_ldap_response_search_entry(&a, msg_tree);
943         break;
944        case LDAP_RES_SEARCH_RESULT:
945        case LDAP_RES_MODIFY:
946        case LDAP_RES_ADD:
947        case LDAP_RES_DELETE:
948        case LDAP_RES_MODRDN:
949        case LDAP_RES_COMPARE:
950         dissect_ldap_result(&a, msg_tree);
951         break;
952       }
953     }
954   }
955 }
956
957 void
958 proto_register_ldap(void)
959 {
960   static value_string result_codes[] = {
961     {0, "Success"},
962     {1, "Operations error"},
963     {2, "Protocol error"},
964     {3, "Time limit exceeded"},
965     {4, "Size limit exceeded"},
966     {5, "Compare false"},
967     {6, "Compare true"},
968     {7, "Authentication method not supported"},
969     {8, "Strong authentication required"},
970     {10, "Referral"},
971     {11, "Administrative limit exceeded"},
972     {12, "Unavailable critical extension"},
973     {13, "Confidentiality required"},
974     {14, "SASL bind in progress"},
975     {16, "No such attribute"},
976     {17, "Undefined attribute type"},
977     {18, "Inappropriate matching"},
978     {19, "Constraint violation"},
979     {20, "Attribute or value exists"},
980     {21, "Invalid attribute syntax"},
981     {32, "No such object"},
982     {33, "Alias problem"},
983     {34, "Invalid DN syntax"},
984     {36, "Alias derefetencing problem"},
985     {48, "Inappropriate authentication"},
986     {49, "Invalid credentials"},
987     {50, "Insufficient access rights"},
988     {51, "Busy"},
989     {52, "Unavailable"},
990     {53, "Unwilling to perform"},
991     {54, "Loop detected"},
992     {64, "Naming violation"},
993     {65, "Objectclass violation"},
994     {66, "Not allowed on non-leaf"},
995     {67, "Not allowed on RDN"},
996     {68, "Entry already exists"},
997     {69, "Objectclass modification prohibited"},
998     {71, "Affects multiple DSAs"},
999     {80, "Other"},
1000   };
1001
1002   static value_string auth_types[] = {
1003     {LDAP_AUTH_SIMPLE,    "Simple"},
1004     {LDAP_AUTH_KRBV4LDAP, "Kerberos V4 to the LDAP server"},
1005     {LDAP_AUTH_KRBV4DSA,  "Kerberos V4 to the DSA"},
1006     {LDAP_AUTH_SASL,      "SASL"},
1007   };
1008   
1009   static value_string search_scope[] = {
1010     {0x00, "Base"},
1011     {0x01, "Single"},
1012     {0x02, "Subtree"},
1013   };
1014     
1015   static value_string search_dereference[] = {
1016     {0x00, "Never"},
1017     {0x01, "Searching"},
1018     {0x02, "Base Object"},
1019     {0x03, "Always"},
1020   };
1021   
1022   static hf_register_info hf[] = {
1023     { &hf_ldap_length,
1024       { "Length",               "ldap.length",
1025         FT_UINT32, BASE_DEC, NULL, 0x0,
1026         "LDAP Length" }},
1027           
1028     { &hf_ldap_message_id,
1029       { "Message Id",           "ldap.message_id",
1030         FT_UINT32, BASE_DEC, NULL, 0x0,
1031         "LDAP Message Id" }},
1032     { &hf_ldap_message_type,
1033       { "Message Type",         "ldap.message_type",
1034         FT_UINT8, BASE_HEX, &msgTypes, 0x0,
1035         "LDAP Message Type" }},
1036     { &hf_ldap_message_length,
1037       { "Message Length",               "ldap.message_length",
1038         FT_UINT32, BASE_DEC, NULL, 0x0,
1039         "LDAP Message Length" }},
1040
1041     { &hf_ldap_message_result,
1042       { "Result Code",          "ldap.result.code",
1043         FT_UINT8, BASE_HEX, result_codes, 0x0,
1044         "LDAP Result Code" }},
1045     { &hf_ldap_message_result_matcheddn,
1046       { "Matched DN",           "ldap.result.matcheddn",
1047         FT_STRING, BASE_NONE, NULL, 0x0,
1048         "LDAP Result Matched DN" }},
1049     { &hf_ldap_message_result_errormsg,
1050       { "Error Message",                "ldap.result.errormsg",
1051         FT_STRING, BASE_NONE, NULL, 0x0,
1052         "LDAP Result Error Message" }},
1053     { &hf_ldap_message_result_referral,
1054       { "Referral",             "ldap.result.referral",
1055         FT_STRING, BASE_NONE, NULL, 0x0,
1056         "LDAP Result Referral URL" }},
1057
1058     { &hf_ldap_message_bind_version,
1059       { "Version",              "ldap.bind.version",
1060         FT_UINT32, BASE_DEC, NULL, 0x0,
1061         "LDAP Bind Version" }},
1062     { &hf_ldap_message_bind_dn,
1063       { "DN",                   "ldap.bind.dn",
1064         FT_STRING, BASE_NONE, NULL, 0x0,
1065         "LDAP Bind Distinguished Name" }},
1066     { &hf_ldap_message_bind_auth,
1067       { "Auth Type",            "ldap.bind.auth_type",
1068         FT_UINT8, BASE_HEX, auth_types, 0x0,
1069         "LDAP Bind Auth Type" }},
1070     { &hf_ldap_message_bind_auth_password,
1071       { "Password",             "ldap.bind.password",
1072         FT_STRING, BASE_NONE, NULL, 0x0,
1073         "LDAP Bind Password" }},
1074
1075     { &hf_ldap_message_search_base,
1076       { "Base DN",              "ldap.search.basedn",
1077         FT_STRING, BASE_NONE, NULL, 0x0,
1078         "LDAP Search Base Distinguished Name" }},
1079     { &hf_ldap_message_search_scope,
1080       { "Scope",                        "ldap.search.scope",
1081         FT_UINT8, BASE_HEX, search_scope, 0x0,
1082         "LDAP Search Scope" }},
1083     { &hf_ldap_message_search_deref,
1084       { "Dereference",          "ldap.search.dereference",
1085         FT_UINT8, BASE_HEX, search_dereference, 0x0,
1086         "LDAP Search Dereference" }},
1087     { &hf_ldap_message_search_sizeLimit,
1088       { "Size Limit",           "ldap.search.sizelimit",
1089         FT_UINT32, BASE_DEC, NULL, 0x0,
1090         "LDAP Search Size Limit" }},
1091     { &hf_ldap_message_search_timeLimit,
1092       { "Time Limit",           "ldap.search.timelimit",
1093         FT_UINT32, BASE_DEC, NULL, 0x0,
1094         "LDAP Search Time Limit" }},
1095     { &hf_ldap_message_search_typesOnly,
1096       { "Attributes Only",      "ldap.search.typesonly",
1097         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1098         "LDAP Search Attributes Only" }},
1099     { &hf_ldap_message_search_filter,
1100       { "Filter",               "ldap.search.filter",
1101         FT_STRING, BASE_NONE, NULL, 0x0,
1102         "LDAP Search Filter" }},
1103     { &hf_ldap_message_dn,
1104       { "Distinguished Name",   "ldap.dn",
1105         FT_STRING, BASE_NONE, NULL, 0x0,
1106         "LDAP Distinguished Name" }},
1107     { &hf_ldap_message_attribute,
1108       { "Attribute",            "ldap.attribute",
1109         FT_STRING, BASE_NONE, NULL, 0x0,
1110         "LDAP Attribute" }},
1111     { &hf_ldap_message_value,
1112       { "Value",                "ldap.value",
1113         FT_STRING, BASE_NONE, NULL, 0x0,
1114         "LDAP Value" }},
1115
1116     { &hf_ldap_message_modrdn_name,
1117       { "New Name",             "ldap.modrdn.name",
1118         FT_STRING, BASE_NONE, NULL, 0x0,
1119         "LDAP New Name" }},
1120     { &hf_ldap_message_modrdn_delete,
1121       { "Delete Values",        "ldap.modrdn.delete",
1122         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1123         "LDAP Modify RDN - Delete original values" }},
1124     { &hf_ldap_message_modrdn_superior,
1125       { "New Location",         "ldap.modrdn.superior",
1126         FT_STRING, BASE_NONE, NULL, 0x0,
1127         "LDAP Modify RDN - New Location" }},
1128
1129     { &hf_ldap_message_compare,
1130       { "Test",         "ldap.compare.test",
1131         FT_STRING, BASE_NONE, NULL, 0x0,
1132         "LDAP Compare Test" }},
1133
1134     { &hf_ldap_message_modify_add,
1135       { "Add",                  "ldap.modify.add",
1136         FT_STRING, BASE_NONE, NULL, 0x0,
1137         "LDAP Add" }},
1138     { &hf_ldap_message_modify_replace,
1139       { "Replace",              "ldap.modify.replace",
1140         FT_STRING, BASE_NONE, NULL, 0x0,
1141         "LDAP Replace" }},
1142     { &hf_ldap_message_modify_delete,
1143       { "Delete",               "ldap.modify.delete",
1144         FT_STRING, BASE_NONE, NULL, 0x0,
1145         "LDAP Delete" }},
1146
1147     { &hf_ldap_message_abandon_msgid,
1148       { "Abandon Msg Id",       "ldap.abandon.msgid",
1149         FT_UINT32, BASE_DEC, NULL, 0x0,
1150         "LDAP Abandon Msg Id" }},
1151   };
1152
1153   static gint *ett[] = {
1154     &ett_ldap,
1155     &ett_ldap_message,
1156     &ett_ldap_referrals,
1157     &ett_ldap_attribute
1158   };
1159
1160   proto_ldap = proto_register_protocol("Lightweight Directory Access Protocol", "ldap");
1161   proto_register_field_array(proto_ldap, hf, array_length(hf));
1162   proto_register_subtree_array(ett, array_length(ett));
1163 }
1164
1165 void
1166 proto_reg_handoff_ldap(void)
1167 {
1168   dissector_add("tcp.port", TCP_PORT_LDAP, dissect_ldap);
1169 }