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