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