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