Add tvbuff class.
[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.10 2000/05/11 08:15:21 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 the substring and 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_item(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_item(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 void read_string_value(ASN1_SCK *a, proto_tree *tree, int hf_id,
232         proto_tree **new_tree, char **s, const guchar *start, guint length)
233 {
234   guchar *string;
235   
236   if (length)
237   {
238     asn1_octet_string_value_decode(a, length, &string);
239     string = g_realloc(string, length + 1);
240     string[length] = '\0';
241   }
242   else
243     string = "(null)";
244     
245   if (tree)
246   {
247     proto_tree *temp_tree;
248     temp_tree = proto_tree_add_item(tree, hf_id, NullTVB, start - a->begin, a->pointer - start, string);
249     if (new_tree)
250       *new_tree = temp_tree;
251   }
252
253   if (s && length)
254     *s = string;
255   else if (length)
256     g_free(string);
257 }
258
259 static int read_string(ASN1_SCK *a, proto_tree *tree, int hf_id,
260         proto_tree **new_tree, char **s, guint expected_cls, guint expected_tag)
261 {
262   guint cls, con, tag;
263   gboolean def;
264   guint length;
265   const guchar *start = a->pointer;
266   int ret;
267   
268   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
269   if (ret != ASN1_ERR_NOERROR)
270     return ret;
271   if (cls != expected_cls || con != ASN1_PRI || tag != expected_tag)
272     return ASN1_ERR_WRONG_TYPE;
273
274   read_string_value(a, tree, hf_id, new_tree, s, start, length);
275   return ASN1_ERR_NOERROR;
276 }
277
278 static int parse_filter_strings(ASN1_SCK *a, char **filter, guint *filter_length, const guchar *operation)
279 {
280   guchar *string;
281   guchar *string2;
282   gint string_length;
283   gint string2_length;
284   guint string_bytes;
285   int ret;
286
287   ret = asn1_octet_string_decode(a, &string, &string_length, &string_bytes);
288   if (ret != ASN1_ERR_NOERROR)
289     return ret;
290   ret = asn1_octet_string_decode(a, &string2, &string2_length, &string_bytes);
291   if (ret != ASN1_ERR_NOERROR)
292     return ret;
293   *filter_length += 2 + strlen(operation) + string_length + string2_length;
294   *filter = g_realloc(*filter, *filter_length);
295   sprintf(*filter + strlen(*filter), "(%.*s%s%.*s)", string_length, string, operation, string2_length, string2);
296   g_free(string);
297   g_free(string2);
298   return ASN1_ERR_NOERROR;
299 }
300
301 /* Returns -1 if we're at the end, returns an ASN1_ERR value otherwise. */
302 static int parse_filter(ASN1_SCK *a, char **filter, guint *filter_length, const guchar **end)
303 {
304   guint cls, con, tag;
305   guint length;
306   gboolean def;
307   int ret;
308
309   ret = asn1_header_decode(a, &cls, &con, &tag, &def, &length);
310   if (ret != ASN1_ERR_NOERROR)
311     return ret;
312   
313   if (*end == 0)
314   {
315     *end = a->pointer + length;
316     *filter_length = 1;
317     *filter = g_malloc0(*filter_length);
318   }
319
320   if (cls == ASN1_CTX)  /* XXX - handle other types as errors? */
321   {
322     switch (tag)
323     {
324      case LDAP_FILTER_AND:
325       {
326         const guchar *add_end;
327
328         if (con != ASN1_CON)
329           return ASN1_ERR_WRONG_TYPE;
330         add_end = a->pointer + length;
331         *filter_length += 3;
332         *filter = g_realloc(*filter, *filter_length);
333         strcat(*filter, "(&");
334         while ((ret = parse_filter(a, filter, filter_length, &add_end))
335                 == ASN1_ERR_NOERROR)
336           continue;
337         if (ret != -1)
338           return ret;
339         strcat(*filter, ")");
340       }
341       break;
342      case LDAP_FILTER_OR:
343       {
344         const guchar *or_end;
345
346         if (con != ASN1_CON)
347           return ASN1_ERR_WRONG_TYPE;
348         or_end = a->pointer + length;
349         *filter_length += 3;
350         *filter = g_realloc(*filter, *filter_length);
351         strcat(*filter, "(|");
352         while ((ret = parse_filter(a, filter, filter_length, &or_end))
353                 == ASN1_ERR_NOERROR)
354           continue;
355         if (ret != -1)
356           return ret;
357         strcat(*filter, ")");
358       }
359       break;
360      case LDAP_FILTER_NOT:
361       {
362         const guchar *not_end;
363
364         if (con != ASN1_CON)
365           return ASN1_ERR_WRONG_TYPE;
366         not_end = a->pointer + length;
367         *filter_length += 3;
368         *filter = g_realloc(*filter, *filter_length);
369         strcat(*filter, "(!");
370         ret = parse_filter(a, filter, filter_length, &not_end);
371         if (ret != -1 && ret != ASN1_ERR_NOERROR)
372           return ret;
373         strcat(*filter, ")");
374       }
375       break;
376      case LDAP_FILTER_EQUALITY:
377       if (con != ASN1_CON)
378         return ASN1_ERR_WRONG_TYPE;
379       ret = parse_filter_strings(a, filter, filter_length, "=");
380       if (ret != -1 && ret != ASN1_ERR_NOERROR)
381         return ret;
382       break;
383      case LDAP_FILTER_GE:
384       if (con != ASN1_CON)
385         return ASN1_ERR_WRONG_TYPE;
386       ret = parse_filter_strings(a, filter, filter_length, ">=");
387       if (ret != -1 && ret != ASN1_ERR_NOERROR)
388         return ret;
389       break;
390      case LDAP_FILTER_LE:
391       if (con != ASN1_CON)
392         return ASN1_ERR_WRONG_TYPE;
393       ret = parse_filter_strings(a, filter, filter_length, "<=");
394       if (ret != -1 && ret != ASN1_ERR_NOERROR)
395         return ret;
396       break;
397      case LDAP_FILTER_APPROX:
398       if (con != ASN1_CON)
399         return ASN1_ERR_WRONG_TYPE;
400       ret = parse_filter_strings(a, filter, filter_length, "~=");
401       if (ret != -1 && ret != ASN1_ERR_NOERROR)
402         return ret;
403       break;
404      case LDAP_FILTER_PRESENT:
405       {
406         guchar *string;
407     
408         if (con != ASN1_PRI)
409           return ASN1_ERR_WRONG_TYPE;
410         ret = asn1_octet_string_value_decode(a, length, &string);
411         if (ret != ASN1_ERR_NOERROR)
412           return ret;
413         *filter_length += 4 + length;
414         *filter = g_realloc(*filter, *filter_length);
415         sprintf(*filter + strlen(*filter), "(%.*s=*)", (int)length, string);
416         g_free(string);
417       }
418       break;
419      case LDAP_FILTER_SUBSTRINGS:
420       if (con != ASN1_CON)
421         return ASN1_ERR_WRONG_TYPE;
422       asn1_null_decode(a, length);      /* XXX - actually decode this... */
423       break;
424      default:
425       return ASN1_ERR_WRONG_TYPE;
426     }
427   }
428   
429   if (a->pointer == *end)
430     return -1;
431   else
432     return ret;
433 }
434
435 static int read_filter(ASN1_SCK *a, proto_tree *tree, int hf_id)
436 {
437   const guchar *start = a->pointer;
438   char *filter = 0;
439   guint filter_length = 0;
440   const guchar *end = 0;
441   int ret;
442      
443   while ((ret = parse_filter(a, &filter, &filter_length, &end))
444         == ASN1_ERR_NOERROR)
445     continue;
446
447   if (tree) {
448     if (ret != -1) {
449       proto_tree_add_text(tree, NullTVB, start-a->begin, 0,
450         "Error parsing filter (%d)", ret);
451     } else
452       proto_tree_add_item(tree, hf_id, NullTVB, start-a->begin, a->pointer-start, filter);
453   }
454
455   g_free(filter);
456
457   return 0;
458 }
459
460 /********************************************************************************************/
461
462 static int dissect_ldap_result(ASN1_SCK *a, proto_tree *tree)
463 {
464   guint resultCode = 0;
465   
466   read_integer(a, tree, hf_ldap_message_result, 0, &resultCode, ASN1_ENUM);
467   read_string(a, tree, hf_ldap_message_result_matcheddn, 0, 0, ASN1_UNI, ASN1_OTS);
468   read_string(a, tree, hf_ldap_message_result_errormsg, 0, 0, ASN1_UNI, ASN1_OTS);
469
470   if (resultCode == 10)         /* Referral */
471   {
472     const guchar *start = a->pointer;
473     const guchar *end;
474     guint length;
475     proto_tree *t, *referralTree;
476     
477     read_sequence(a, &length);
478     t = proto_tree_add_text(tree, NullTVB, start-a->begin, length, "Referral URLs");
479     referralTree = proto_item_add_subtree(t, ett_ldap_referrals);
480
481     end = a->pointer + length;;
482     while (a->pointer < end)
483       read_string(a, referralTree, hf_ldap_message_result_referral, 0, 0, ASN1_UNI, ASN1_OTS);
484   }
485     
486   return 0;
487 }
488
489 static int dissect_ldap_request_bind(ASN1_SCK *a, proto_tree *tree)
490 {
491   guint cls, con, tag;
492   guint def, length;
493   const guchar *start;
494
495   read_integer(a, tree, hf_ldap_message_bind_version, 0, 0, ASN1_INT);
496   read_string(a, tree, hf_ldap_message_bind_dn, 0, 0, ASN1_UNI, ASN1_OTS);
497
498   start = a->pointer;
499   if (asn1_header_decode(a, &cls, &con, &tag, &def, &length) != ASN1_ERR_NOERROR)
500     return 1;   /* XXX - right return value for an error? */
501   if (cls != ASN1_CTX)
502     return 1;   /* RFCs 1777 and 2251 say these are context-specific types */
503   proto_tree_add_item(tree, hf_ldap_message_bind_auth, NullTVB, start - a->begin,
504                         a->pointer - start, tag);
505   switch (tag)
506   {
507    case LDAP_AUTH_SIMPLE:
508     read_string_value(a, tree, hf_ldap_message_bind_auth_password, NULL, NULL,
509                         start, length);
510     break;
511
512     /* For Kerberos V4, dissect it as a ticket. */
513     /* For SASL, dissect it as SaslCredentials. */
514   }
515   
516   return 0;
517 }
518
519 static int dissect_ldap_response_bind(ASN1_SCK *a, proto_tree *tree)
520 {
521   dissect_ldap_result(a, tree);
522   /* FIXME: handle SASL data */
523   return 0;
524 }
525
526 static int dissect_ldap_request_search(ASN1_SCK *a, proto_tree *tree)
527 {
528   guint seq_length;
529   const guchar *end;
530   int ret;
531   
532   read_string(a, tree, hf_ldap_message_search_base, 0, 0, ASN1_UNI, ASN1_OTS);
533   read_integer(a, tree, hf_ldap_message_search_scope, 0, 0, ASN1_ENUM);
534   read_integer(a, tree, hf_ldap_message_search_deref, 0, 0, ASN1_ENUM);
535   read_integer(a, tree, hf_ldap_message_search_sizeLimit, 0, 0, ASN1_INT);
536   read_integer(a, tree, hf_ldap_message_search_timeLimit, 0, 0, ASN1_INT);
537   read_integer(a, tree, hf_ldap_message_search_typesOnly, 0, 0, ASN1_BOL);
538   ret = read_filter(a, tree, hf_ldap_message_search_filter);
539   if (ret != ASN1_ERR_NOERROR)
540     return ret;
541   read_sequence(a, &seq_length);
542   end = a->pointer + seq_length;
543   while (a->pointer < end) {
544     ret = read_string(a, tree, hf_ldap_message_attribute, 0, 0, ASN1_UNI, ASN1_OTS);
545     if (ret != ASN1_ERR_NOERROR)
546       return ret;
547   }
548   return ASN1_ERR_NOERROR;
549 }
550
551 static int dissect_ldap_response_search_entry(ASN1_SCK *a, proto_tree *tree)
552 {
553   guint seq_length;
554   const guchar *end_of_sequence;
555  
556   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
557   read_sequence(a, &seq_length);
558
559   end_of_sequence = a->pointer + seq_length;
560   while (a->pointer < end_of_sequence)
561   {
562     proto_tree *t, *attr_tree;
563     guint set_length;
564     const guchar *end_of_set;
565
566     read_sequence(a, 0);
567     read_string(a, tree, hf_ldap_message_attribute, &t, 0, ASN1_UNI, ASN1_OTS);
568     attr_tree = proto_item_add_subtree(t, ett_ldap_attribute);
569
570     read_set(a, &set_length);
571     end_of_set = a->pointer + set_length;
572     while (a->pointer < end_of_set)
573       read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS);
574   }
575
576   return 0;
577 }
578
579 static int dissect_ldap_request_add(ASN1_SCK *a, proto_tree *tree)
580 {
581   guint seq_length;
582   const guchar *end_of_sequence;
583   
584   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
585
586   read_sequence(a, &seq_length);
587   end_of_sequence = a->pointer + seq_length;
588   while (a->pointer < end_of_sequence)
589   {
590     proto_tree *t, *attr_tree;
591     guint set_length;
592     const guchar *end_of_set;
593
594     read_sequence(a, 0);
595     read_string(a, tree, hf_ldap_message_attribute, &t, 0, ASN1_UNI, ASN1_OTS);
596     attr_tree = proto_item_add_subtree(t, ett_ldap_attribute);
597
598     read_set(a, &set_length);
599     end_of_set = a->pointer + set_length;
600     while (a->pointer < end_of_set)
601       read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS);
602   }
603
604   return 0;
605 }
606
607 static int dissect_ldap_request_delete(ASN1_SCK *a, proto_tree *tree,
608                 const guchar *start, guint length)
609 {
610   read_string_value(a, tree, hf_ldap_message_dn, NULL, NULL, start, length);
611   return 0;
612 }
613
614 static int dissect_ldap_request_modifyrdn(ASN1_SCK *a, proto_tree *tree,
615                 guint length)
616 {
617   const guchar *start = a->pointer;
618
619   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
620   read_string(a, tree, hf_ldap_message_modrdn_name, 0, 0, ASN1_UNI, ASN1_OTS);
621   read_integer(a, tree, hf_ldap_message_modrdn_delete, 0, 0, ASN1_BOL);
622   
623   if (a->pointer < (start + length)) {
624     /* LDAP V3 Modify DN operation, with newSuperior */
625     read_string(a, tree, hf_ldap_message_modrdn_superior, 0, 0, ASN1_UNI, ASN1_OTS);
626   }
627
628   return 0;
629 }
630
631 static int dissect_ldap_request_compare(ASN1_SCK *a, proto_tree *tree)
632 {
633   const guchar *start;
634   int length;
635   char *string1 = 0;
636   char *string2 = 0;
637   char *compare;
638   
639   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
640   read_sequence(a, 0);
641
642   start = a->pointer;
643   read_string(a, 0, -1, 0, &string1, ASN1_UNI, ASN1_OTS);
644   read_string(a, 0, -1, 0, &string2, ASN1_UNI, ASN1_OTS);
645
646   length = 2 + strlen(string1) + strlen(string2);
647   compare = g_malloc0(length);
648   snprintf(compare, length, "%s=%s", string1, string2);
649   proto_tree_add_item(tree, hf_ldap_message_compare, NullTVB, start-a->begin, a->pointer-start, compare);
650   
651   g_free(string1);
652   g_free(string2);
653   g_free(compare);
654   
655   return 0;
656 }
657
658 static int dissect_ldap_request_modify(ASN1_SCK *a, proto_tree *tree)
659 {
660   guint seq_length;
661   const guchar *end_of_sequence;
662   
663   read_string(a, tree, hf_ldap_message_dn, 0, 0, ASN1_UNI, ASN1_OTS);
664   read_sequence(a, &seq_length);
665   end_of_sequence = a->pointer + seq_length;
666   while (a->pointer < end_of_sequence)
667   {
668     proto_tree *t = 0, *attr_tree;
669     guint set_length;
670     const guchar *end_of_set;
671     guint operation;
672
673     read_sequence(a, 0);
674     read_integer(a, 0, -1, 0, &operation, ASN1_ENUM);
675     read_sequence(a, 0);
676
677     switch (operation)
678     {
679      case LDAP_MOD_ADD:
680       read_string(a, tree, hf_ldap_message_modify_add, &t, 0, ASN1_UNI, ASN1_OTS);
681       break;
682      case LDAP_MOD_REPLACE:
683       read_string(a, tree, hf_ldap_message_modify_replace, &t, 0, ASN1_UNI, ASN1_OTS);
684       break;
685      case LDAP_MOD_DELETE:
686       read_string(a, tree, hf_ldap_message_modify_delete, &t, 0, ASN1_UNI, ASN1_OTS);
687       break;
688     }
689     attr_tree = proto_item_add_subtree(t, ett_ldap_attribute);
690
691     read_set(a, &set_length);
692     end_of_set = a->pointer + set_length;
693     while (a->pointer < end_of_set)
694       read_string(a, attr_tree, hf_ldap_message_value, 0, 0, ASN1_UNI, ASN1_OTS);
695   }
696
697   return 0;
698 }
699
700 static int dissect_ldap_request_abandon(ASN1_SCK *a, proto_tree *tree,
701                 const guchar *start, guint length)
702 {
703   read_integer_value(a, tree, hf_ldap_message_abandon_msgid, NULL, NULL,
704                         start, length); 
705   return 0;
706 }
707
708 static void
709 dissect_ldap(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
710 {
711   proto_tree *ldap_tree = 0, *ti, *msg_tree;
712   guint messageLength;
713   guint messageId;
714   guint protocolOpCls, protocolOpCon, protocolOpTag;
715   gchar *typestr;
716   guint opLen;
717   ASN1_SCK a;
718   const guchar *start;
719   int first_time = 1;
720   int ret;
721
722   if (tree) 
723   {
724     ti = proto_tree_add_item(tree, proto_ldap, NullTVB, offset, END_OF_FRAME, NULL);
725     ldap_tree = proto_item_add_subtree(ti, ett_ldap);
726   }
727
728   asn1_open(&a, pd, pi.captured_len);
729   a.pointer += offset;
730
731   while (a.pointer < a.end)
732   {
733     int message_id_start;
734     int message_id_length;
735     int message_start;
736     
737     message_start = a.pointer - a.begin;
738     if (read_sequence(&a, &messageLength))
739     {
740       if (ldap_tree)
741         proto_tree_add_text(ldap_tree, NullTVB, offset, 1, "Invalid LDAP packet");
742       break;
743     }
744
745     if (messageLength > (a.end - a.pointer))
746     {
747       if (ldap_tree)
748         proto_tree_add_text(ldap_tree, NullTVB, message_start, END_OF_FRAME, "Short message! (expected: %u, actual: %u)",
749                             messageLength, a.end - a.pointer);
750       break;
751     }
752   
753     message_id_start = a.pointer - a.begin;
754     read_integer(&a, 0, -1, 0, &messageId, ASN1_INT);
755     message_id_length = (a.pointer - a.begin) - message_id_start;
756
757     start = a.pointer;
758     asn1_id_decode(&a, &protocolOpCls, &protocolOpCon, &protocolOpTag);
759     if (protocolOpCls != ASN1_APL)
760       typestr = "Bad message type (not Application)";
761     else
762       typestr = val_to_str(protocolOpTag, msgTypes, "Bad message type (%u)");
763
764     if (first_time)
765     {
766       if (check_col(fd, COL_PROTOCOL))
767         col_add_str(fd, COL_PROTOCOL, "LDAP");
768
769       if (check_col(fd, COL_INFO))
770         col_add_fstr(fd, COL_INFO, "MsgId=%u MsgType=%s",
771                      messageId, typestr);
772       first_time = 0;
773       if (!tree)
774         return;
775     }
776
777     if (ldap_tree) 
778     {
779       proto_tree_add_item_hidden(ldap_tree, hf_ldap_message_id, NullTVB, message_id_start, message_id_length, messageId);
780       proto_tree_add_item_hidden(ldap_tree, hf_ldap_message_type, NullTVB,
781                                  start - a.begin, a.pointer - start, protocolOpTag);
782       ti = proto_tree_add_text(ldap_tree, NullTVB, message_id_start, messageLength, "Message: Id=%u  %s", messageId, typestr);
783       msg_tree = proto_item_add_subtree(ti, ett_ldap_message);
784       start = a.pointer;
785       read_length(&a, msg_tree, hf_ldap_message_length, &opLen);
786
787       switch (protocolOpTag)
788       {
789        case LDAP_REQ_BIND:
790         dissect_ldap_request_bind(&a, msg_tree);
791         break;
792        case LDAP_REQ_SEARCH:
793         ret = dissect_ldap_request_search(&a, msg_tree);
794         if (ret != ASN1_ERR_NOERROR)
795           break;
796         break;
797        case LDAP_REQ_ADD:
798         dissect_ldap_request_add(&a, msg_tree);
799         break;
800        case LDAP_REQ_DELETE:
801         dissect_ldap_request_delete(&a, msg_tree, start, opLen);
802         break;
803        case LDAP_REQ_MODRDN:
804         dissect_ldap_request_modifyrdn(&a, msg_tree, opLen);
805         break;
806        case LDAP_REQ_COMPARE:
807         dissect_ldap_request_compare(&a, msg_tree);
808         break;
809        case LDAP_REQ_MODIFY:
810         dissect_ldap_request_modify(&a, msg_tree);
811         break;
812        case LDAP_REQ_ABANDON:
813         dissect_ldap_request_abandon(&a, msg_tree, start, opLen);
814         break;
815        case LDAP_RES_BIND:
816         dissect_ldap_response_bind(&a, msg_tree);
817         break;
818        case LDAP_RES_SEARCH_ENTRY:
819         dissect_ldap_response_search_entry(&a, msg_tree);
820         break;
821        case LDAP_RES_SEARCH_RESULT:
822        case LDAP_RES_MODIFY:
823        case LDAP_RES_ADD:
824        case LDAP_RES_DELETE:
825        case LDAP_RES_MODRDN:
826        case LDAP_RES_COMPARE:
827         dissect_ldap_result(&a, msg_tree);
828         break;
829       }
830     }
831   }
832 }
833
834 void
835 proto_register_ldap(void)
836 {
837   static value_string result_codes[] = {
838     {0, "Success"},
839     {1, "Operations error"},
840     {2, "Protocol error"},
841     {3, "Time limit exceeded"},
842     {4, "Size limit exceeded"},
843     {5, "Compare false"},
844     {6, "Compare true"},
845     {7, "Authentication method not supported"},
846     {8, "Strong authentication required"},
847     {10, "Referral"},
848     {11, "Administrative limit exceeded"},
849     {12, "Unavailable critical extension"},
850     {13, "Confidentiality required"},
851     {14, "SASL bind in progress"},
852     {16, "No such attribute"},
853     {17, "Undefined attribute type"},
854     {18, "Inappropriate matching"},
855     {19, "Constraint violation"},
856     {20, "Attribute or value exists"},
857     {21, "Invalid attribute syntax"},
858     {32, "No such object"},
859     {33, "Alias problem"},
860     {34, "Invalid DN syntax"},
861     {36, "Alias derefetencing problem"},
862     {48, "Inappropriate authentication"},
863     {49, "Invalid credentials"},
864     {50, "Insufficient access rights"},
865     {51, "Busy"},
866     {52, "Unavailable"},
867     {53, "Unwilling to perform"},
868     {54, "Loop detected"},
869     {64, "Naming violation"},
870     {65, "Objectclass violation"},
871     {66, "Not allowed on non-leaf"},
872     {67, "Not allowed on RDN"},
873     {68, "Entry already exists"},
874     {69, "Objectclass modification prohibited"},
875     {71, "Affects multiple DSAs"},
876     {80, "Other"},
877   };
878
879   static value_string auth_types[] = {
880     {LDAP_AUTH_SIMPLE,    "Simple"},
881     {LDAP_AUTH_KRBV4LDAP, "Kerberos V4 to the LDAP server"},
882     {LDAP_AUTH_KRBV4DSA,  "Kerberos V4 to the DSA"},
883     {LDAP_AUTH_SASL,      "SASL"},
884   };
885   
886   static value_string search_scope[] = {
887     {0x00, "Base"},
888     {0x01, "Single"},
889     {0x02, "Subtree"},
890   };
891     
892   static value_string search_dereference[] = {
893     {0x00, "Never"},
894     {0x01, "Searching"},
895     {0x02, "Base Object"},
896     {0x03, "Always"},
897   };
898   
899   static hf_register_info hf[] = {
900     { &hf_ldap_length,
901       { "Length",               "ldap.length",
902         FT_INT32, BASE_DEC, NULL, 0x0,
903         "LDAP Length" }},
904           
905     { &hf_ldap_message_id,
906       { "Message Id",           "ldap.message_id",
907         FT_INT32, BASE_DEC, NULL, 0x0,
908         "LDAP Message Id" }},
909     { &hf_ldap_message_type,
910       { "Message Type",         "ldap.message_type",
911         FT_UINT8, BASE_HEX, &msgTypes, 0x0,
912         "LDAP Message Type" }},
913     { &hf_ldap_message_length,
914       { "Message Length",               "ldap.message_length",
915         FT_INT32, BASE_DEC, NULL, 0x0,
916         "LDAP Message Length" }},
917
918     { &hf_ldap_message_result,
919       { "Result Code",          "ldap.result.code",
920         FT_INT8, BASE_HEX, result_codes, 0x0,
921         "LDAP Result Code" }},
922     { &hf_ldap_message_result_matcheddn,
923       { "Matched DN",           "ldap.result.matcheddn",
924         FT_STRING, BASE_NONE, NULL, 0x0,
925         "LDAP Result Matched DN" }},
926     { &hf_ldap_message_result_errormsg,
927       { "Error Message",                "ldap.result.errormsg",
928         FT_STRING, BASE_NONE, NULL, 0x0,
929         "LDAP Result Error Message" }},
930     { &hf_ldap_message_result_referral,
931       { "Referral",             "ldap.result.referral",
932         FT_STRING, BASE_NONE, NULL, 0x0,
933         "LDAP Result Referral URL" }},
934
935     { &hf_ldap_message_bind_version,
936       { "Version",              "ldap.bind.version",
937         FT_INT32, BASE_DEC, NULL, 0x0,
938         "LDAP Bind Version" }},
939     { &hf_ldap_message_bind_dn,
940       { "DN",                   "ldap.bind.dn",
941         FT_STRING, BASE_NONE, NULL, 0x0,
942         "LDAP Bind Distinguished Name" }},
943     { &hf_ldap_message_bind_auth,
944       { "Auth Type",            "ldap.bind.auth_type",
945         FT_UINT8, BASE_HEX, auth_types, 0x0,
946         "LDAP Bind Auth Type" }},
947     { &hf_ldap_message_bind_auth_password,
948       { "Password",             "ldap.bind.password",
949         FT_STRING, BASE_NONE, NULL, 0x0,
950         "LDAP Bind Password" }},
951
952     { &hf_ldap_message_search_base,
953       { "Base DN",              "ldap.search.basedn",
954         FT_STRING, BASE_NONE, NULL, 0x0,
955         "LDAP Search Base Distinguished Name" }},
956     { &hf_ldap_message_search_scope,
957       { "Scope",                        "ldap.search.scope",
958         FT_UINT8, BASE_HEX, search_scope, 0x0,
959         "LDAP Search Scope" }},
960     { &hf_ldap_message_search_deref,
961       { "Dereference",          "ldap.search.dereference",
962         FT_UINT8, BASE_HEX, search_dereference, 0x0,
963         "LDAP Search Dereference" }},
964     { &hf_ldap_message_search_sizeLimit,
965       { "Size Limit",           "ldap.search.sizelimit",
966         FT_INT32, BASE_DEC, NULL, 0x0,
967         "LDAP Search Size Limit" }},
968     { &hf_ldap_message_search_timeLimit,
969       { "Time Limit",           "ldap.search.timelimit",
970         FT_INT32, BASE_DEC, NULL, 0x0,
971         "LDAP Search Time Limit" }},
972     { &hf_ldap_message_search_typesOnly,
973       { "Attributes Only",      "ldap.search.typesonly",
974         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
975         "LDAP Search Attributes Only" }},
976     { &hf_ldap_message_search_filter,
977       { "Filter",               "ldap.search.filter",
978         FT_STRING, BASE_NONE, NULL, 0x0,
979         "LDAP Search Filter" }},
980     { &hf_ldap_message_dn,
981       { "Distinguished Name",   "ldap.dn",
982         FT_STRING, BASE_NONE, NULL, 0x0,
983         "LDAP Distinguished Name" }},
984     { &hf_ldap_message_attribute,
985       { "Attribute",            "ldap.attribute",
986         FT_STRING, BASE_NONE, NULL, 0x0,
987         "LDAP Attribute" }},
988     { &hf_ldap_message_value,
989       { "Value",                "ldap.value",
990         FT_STRING, BASE_NONE, NULL, 0x0,
991         "LDAP Value" }},
992
993     { &hf_ldap_message_modrdn_name,
994       { "New Name",             "ldap.modrdn.name",
995         FT_STRING, BASE_NONE, NULL, 0x0,
996         "LDAP New Name" }},
997     { &hf_ldap_message_modrdn_delete,
998       { "Delete Values",        "ldap.modrdn.delete",
999         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1000         "LDAP Modify RDN - Delete original values" }},
1001     { &hf_ldap_message_modrdn_superior,
1002       { "New Location",         "ldap.modrdn.superior",
1003         FT_STRING, BASE_NONE, NULL, 0x0,
1004         "LDAP Modify RDN - New Location" }},
1005
1006     { &hf_ldap_message_compare,
1007       { "Test",         "ldap.compare.test",
1008         FT_STRING, BASE_NONE, NULL, 0x0,
1009         "LDAP Compare Test" }},
1010
1011     { &hf_ldap_message_modify_add,
1012       { "Add",                  "ldap.modify.add",
1013         FT_STRING, BASE_NONE, NULL, 0x0,
1014         "LDAP Add" }},
1015     { &hf_ldap_message_modify_replace,
1016       { "Replace",              "ldap.modify.replace",
1017         FT_STRING, BASE_NONE, NULL, 0x0,
1018         "LDAP Replace" }},
1019     { &hf_ldap_message_modify_delete,
1020       { "Delete",               "ldap.modify.delete",
1021         FT_STRING, BASE_NONE, NULL, 0x0,
1022         "LDAP Delete" }},
1023
1024     { &hf_ldap_message_abandon_msgid,
1025       { "Abandon Msg Id",       "ldap.abandon.msgid",
1026         FT_INT32, BASE_DEC, NULL, 0x0,
1027         "LDAP Abandon Msg Id" }},
1028   };
1029
1030   static gint *ett[] = {
1031     &ett_ldap,
1032     &ett_ldap_message,
1033     &ett_ldap_referrals,
1034     &ett_ldap_attribute
1035   };
1036
1037   proto_ldap = proto_register_protocol("Lightweight Directory Access Protocol", "ldap");
1038   proto_register_field_array(proto_ldap, hf, array_length(hf));
1039   proto_register_subtree_array(ett, array_length(ett));
1040 }
1041
1042 void
1043 proto_reg_handoff_ldap(void)
1044 {
1045   dissector_add("tcp.port", TCP_PORT_LDAP, dissect_ldap);
1046 }
1047