r26613: Add a function to write a DATA_BLOB into an LDAPString.
[samba.git] / source4 / libcli / ldap / ldap.c
index b2a6bd957a90c77a0494044b5c14f977a53f0f3c..586f2fa6531ca49200fb9720858d15c9ce8b2fe2 100644 (file)
@@ -9,7 +9,7 @@
     
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
 */
 
 #include "includes.h"
-#include "system/iconv.h"
-#include "asn_1.h"
+#include "libcli/util/asn_1.h"
+#include "libcli/ldap/ldap.h"
 
-/****************************************************************************
- *
- * LDAP filter parser -- main routine is ldap_parse_filter
- *
- * Shamelessly stolen and adapted from ldb.
- *
- ***************************************************************************/
 
-/*
-  return next token element. Caller frees
-*/
-static char *ldap_parse_lex(TALLOC_CTX *mem_ctx, const char **s,
-                          const char *sep)
+static bool ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree)
 {
-       const char *p = *s;
-       char *ret;
-
-       while (isspace(*p)) {
-               p++;
-       }
-       *s = p;
-
-       if (*p == 0) {
-               return NULL;
-       }
+       int i;
 
-       if (strchr(sep, *p)) {
-               (*s) = p+1;
-               ret = talloc_strndup(mem_ctx, p, 1);
-               if (!ret) {
-                       errno = ENOMEM;
+       switch (tree->operation) {
+       case LDB_OP_AND:
+       case LDB_OP_OR:
+               asn1_push_tag(data, ASN1_CONTEXT(tree->operation==LDB_OP_AND?0:1));
+               for (i=0; i<tree->u.list.num_elements; i++) {
+                       if (!ldap_push_filter(data, tree->u.list.elements[i])) {
+                               return false;
+                       }
                }
-               return ret;
-       }
-
-       while (*p && (isalnum(*p) || !strchr(sep, *p))) {
-               p++;
-       }
-
-       if (p == *s) {
-               return NULL;
-       }
-
-       ret = talloc_strndup(mem_ctx, *s, p - *s);
-       if (!ret) {
-               errno = ENOMEM;
-       }
-
-       *s = p;
-
-       return ret;
-}
-
+               asn1_pop_tag(data);
+               break;
 
-/*
-  find a matching close brace in a string
-*/
-static const char *match_brace(const char *s)
-{
-       unsigned int count = 0;
-       while (*s && (count != 0 || *s != ')')) {
-               if (*s == '(') {
-                       count++;
-               }
-               if (*s == ')') {
-                       count--;
+       case LDB_OP_NOT:
+               asn1_push_tag(data, ASN1_CONTEXT(2));
+               if (!ldap_push_filter(data, tree->u.isnot.child)) {
+                       return false;
                }
-               s++;
-       }
-       if (! *s) {
-               return NULL;
-       }
-       return s;
-}
-
-static struct ldap_parse_tree *ldap_parse_filter(TALLOC_CTX *mem_ctx,
-                                              const char **s);
-
-/*
-  <simple> ::= <attributetype> <filtertype> <attributevalue>
-*/
-static struct ldap_parse_tree *ldap_parse_simple(TALLOC_CTX *mem_ctx,
-                                              const char *s)
-{
-       char *eq, *val, *l;
-       struct ldap_parse_tree *ret;
-
-       l = ldap_parse_lex(mem_ctx, &s, LDAP_ALL_SEP);
-       if (!l) {
-               return NULL;
-       }
-
-       if (strchr("()&|=", *l))
-               return NULL;
-
-       eq = ldap_parse_lex(mem_ctx, &s, LDAP_ALL_SEP);
-       if (!eq || strcmp(eq, "=") != 0)
-               return NULL;
-
-       val = ldap_parse_lex(mem_ctx, &s, ")");
-       if (val && strchr("()&|", *val))
-               return NULL;
-       
-       ret = talloc_p(mem_ctx, struct ldap_parse_tree);
-       if (!ret) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       ret->operation = LDAP_OP_SIMPLE;
-       ret->u.simple.attr = l;
-       ret->u.simple.value.data = val;
-       ret->u.simple.value.length = val?strlen(val):0;
-
-       return ret;
-}
-
-
-/*
-  parse a filterlist
-  <and> ::= '&' <filterlist>
-  <or> ::= '|' <filterlist>
-  <filterlist> ::= <filter> | <filter> <filterlist>
-*/
-static struct ldap_parse_tree *ldap_parse_filterlist(TALLOC_CTX *mem_ctx,
-                                                  enum ldap_parse_op op,
-                                                  const char *s)
-{
-       struct ldap_parse_tree *ret, *next;
-
-       ret = talloc_p(mem_ctx, struct ldap_parse_tree);
-       if (!ret) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       ret->operation = op;
-       ret->u.list.num_elements = 1;
-       ret->u.list.elements = talloc_p(mem_ctx, struct ldap_parse_tree *);
-       if (!ret->u.list.elements) {
-               errno = ENOMEM;
-               return NULL;
-       }
+               asn1_pop_tag(data);
+               break;
 
-       ret->u.list.elements[0] = ldap_parse_filter(mem_ctx, &s);
-       if (!ret->u.list.elements[0]) {
-               return NULL;
-       }
+       case LDB_OP_EQUALITY:
+               /* equality test */
+               asn1_push_tag(data, ASN1_CONTEXT(3));
+               asn1_write_OctetString(data, tree->u.equality.attr,
+                                     strlen(tree->u.equality.attr));
+               asn1_write_OctetString(data, tree->u.equality.value.data,
+                                     tree->u.equality.value.length);
+               asn1_pop_tag(data);
+               break;
 
-       while (isspace(*s)) s++;
-
-       while (*s && (next = ldap_parse_filter(mem_ctx, &s))) {
-               struct ldap_parse_tree **e;
-               e = talloc_realloc_p(ret,
-                                    ret->u.list.elements,
-                                    struct ldap_parse_tree *,
-                                    ret->u.list.num_elements+1);
-               if (!e) {
-                       errno = ENOMEM;
-                       return NULL;
+       case LDB_OP_SUBSTRING:
+               /*
+                 SubstringFilter ::= SEQUENCE {
+                         type            AttributeDescription,
+                         -- at least one must be present
+                         substrings      SEQUENCE OF CHOICE {
+                                 initial [0] LDAPString,
+                                 any     [1] LDAPString,
+                                 final   [2] LDAPString } }
+               */
+               asn1_push_tag(data, ASN1_CONTEXT(4));
+               asn1_write_OctetString(data, tree->u.substring.attr, strlen(tree->u.substring.attr));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
+               i = 0;
+               if ( ! tree->u.substring.start_with_wildcard) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
+                       asn1_write_DATA_BLOB_LDAPString(data, tree->u.substring.chunks[i]);
+                       asn1_pop_tag(data);
+                       i++;
                }
-               ret->u.list.elements = e;
-               ret->u.list.elements[ret->u.list.num_elements] = next;
-               ret->u.list.num_elements++;
-               while (isspace(*s)) s++;
-       }
-
-       return ret;
-}
-
-
-/*
-  <not> ::= '!' <filter>
-*/
-static struct ldap_parse_tree *ldap_parse_not(TALLOC_CTX *mem_ctx, const char *s)
-{
-       struct ldap_parse_tree *ret;
-
-       ret = talloc_p(mem_ctx, struct ldap_parse_tree);
-       if (!ret) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       ret->operation = LDAP_OP_NOT;
-       ret->u.not.child = ldap_parse_filter(mem_ctx, &s);
-       if (!ret->u.not.child)
-               return NULL;
-
-       return ret;
-}
-
-/*
-  parse a filtercomp
-  <filtercomp> ::= <and> | <or> | <not> | <simple>
-*/
-static struct ldap_parse_tree *ldap_parse_filtercomp(TALLOC_CTX *mem_ctx,
-                                                  const char *s)
-{
-       while (isspace(*s)) s++;
-
-       switch (*s) {
-       case '&':
-               return ldap_parse_filterlist(mem_ctx, LDAP_OP_AND, s+1);
-
-       case '|':
-               return ldap_parse_filterlist(mem_ctx, LDAP_OP_OR, s+1);
-
-       case '!':
-               return ldap_parse_not(mem_ctx, s+1);
-
-       case '(':
-       case ')':
-               return NULL;
-       }
-
-       return ldap_parse_simple(mem_ctx, s);
-}
-
-
-/*
-  <filter> ::= '(' <filtercomp> ')'
-*/
-static struct ldap_parse_tree *ldap_parse_filter(TALLOC_CTX *mem_ctx,
-                                              const char **s)
-{
-       char *l, *s2;
-       const char *p, *p2;
-       struct ldap_parse_tree *ret;
-
-       l = ldap_parse_lex(mem_ctx, s, LDAP_ALL_SEP);
-       if (!l) {
-               return NULL;
-       }
-
-       if (strcmp(l, "(") != 0) {
-               return NULL;
-       }
-
-       p = match_brace(*s);
-       if (!p) {
-               return NULL;
-       }
-       p2 = p + 1;
-
-       s2 = talloc_strndup(mem_ctx, *s, p - *s);
-       if (!s2) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       ret = ldap_parse_filtercomp(mem_ctx, s2);
-
-       *s = p2;
-
-       return ret;
-}
-
-/*
-  main parser entry point. Takes a search string and returns a parse tree
-
-  expression ::= <simple> | <filter>
-*/
-static struct ldap_parse_tree *ldap_parse_tree(TALLOC_CTX *mem_ctx, const char *s)
-{
-       while (isspace(*s)) s++;
-
-       if (*s == '(') {
-               return ldap_parse_filter(mem_ctx, &s);
-       }
-
-       return ldap_parse_simple(mem_ctx, s);
-}
-
-static BOOL ldap_push_filter(struct asn1_data *data, struct ldap_parse_tree *tree)
-{
-       switch (tree->operation) {
-       case LDAP_OP_SIMPLE: {
-               if ((tree->u.simple.value.length == 1) &&
-                   (((char *)(tree->u.simple.value.data))[0] == '*')) {
-                       /* Just a presence test */
-                       asn1_push_tag(data, 0x87);
-                       asn1_write(data, tree->u.simple.attr,
-                                  strlen(tree->u.simple.attr));
+               while (tree->u.substring.chunks[i]) {
+                       int ctx;
+
+                       if (( ! tree->u.substring.chunks[i + 1]) &&
+                           (tree->u.substring.end_with_wildcard == 0)) {
+                               ctx = 2;
+                       } else {
+                               ctx = 1;
+                       }
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(ctx));
+                       asn1_write_DATA_BLOB_LDAPString(data, tree->u.substring.chunks[i]);
                        asn1_pop_tag(data);
-                       return !data->has_error;
+                       i++;
                }
-
-               /* Equality is all we currently do... */
-               asn1_push_tag(data, 0xa3);
-               asn1_write_OctetString(data, tree->u.simple.attr,
-                                     strlen(tree->u.simple.attr));
-               asn1_write_OctetString(data, tree->u.simple.value.data,
-                                     tree->u.simple.value.length);
+               asn1_pop_tag(data);
                asn1_pop_tag(data);
                break;
-       }
 
-       case LDAP_OP_AND: {
-               int i;
+       case LDB_OP_GREATER:
+               /* greaterOrEqual test */
+               asn1_push_tag(data, ASN1_CONTEXT(5));
+               asn1_write_OctetString(data, tree->u.comparison.attr,
+                                     strlen(tree->u.comparison.attr));
+               asn1_write_OctetString(data, tree->u.comparison.value.data,
+                                     tree->u.comparison.value.length);
+               asn1_pop_tag(data);
+               break;
 
-               asn1_push_tag(data, 0xa0);
-               for (i=0; i<tree->u.list.num_elements; i++) {
-                       ldap_push_filter(data, tree->u.list.elements[i]);
-               }
+       case LDB_OP_LESS:
+               /* lessOrEqual test */
+               asn1_push_tag(data, ASN1_CONTEXT(6));
+               asn1_write_OctetString(data, tree->u.comparison.attr,
+                                     strlen(tree->u.comparison.attr));
+               asn1_write_OctetString(data, tree->u.comparison.value.data,
+                                     tree->u.comparison.value.length);
                asn1_pop_tag(data);
                break;
-       }
 
-       case LDAP_OP_OR: {
-               int i;
+       case LDB_OP_PRESENT:
+               /* present test */
+               asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(7));
+               asn1_write_LDAPString(data, tree->u.present.attr);
+               asn1_pop_tag(data);
+               return !data->has_error;
+
+       case LDB_OP_APPROX:
+               /* approx test */
+               asn1_push_tag(data, ASN1_CONTEXT(8));
+               asn1_write_OctetString(data, tree->u.comparison.attr,
+                                     strlen(tree->u.comparison.attr));
+               asn1_write_OctetString(data, tree->u.comparison.value.data,
+                                     tree->u.comparison.value.length);
+               asn1_pop_tag(data);
+               break;
 
-               asn1_push_tag(data, 0xa1);
-               for (i=0; i<tree->u.list.num_elements; i++) {
-                       ldap_push_filter(data, tree->u.list.elements[i]);
+       case LDB_OP_EXTENDED:
+               /*
+                 MatchingRuleAssertion ::= SEQUENCE {
+                 matchingRule    [1] MatchingRuleID OPTIONAL,
+                 type            [2] AttributeDescription OPTIONAL,
+                 matchValue      [3] AssertionValue,
+                 dnAttributes    [4] BOOLEAN DEFAULT FALSE
+                 }
+               */
+               asn1_push_tag(data, ASN1_CONTEXT(9));
+               if (tree->u.extended.rule_id) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(1));
+                       asn1_write_LDAPString(data, tree->u.extended.rule_id);
+                       asn1_pop_tag(data);
+               }
+               if (tree->u.extended.attr) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(2));
+                       asn1_write_LDAPString(data, tree->u.extended.attr);
+                       asn1_pop_tag(data);
                }
+               asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(3));
+               asn1_write_DATA_BLOB_LDAPString(data, &tree->u.extended.value);
+               asn1_pop_tag(data);
+               asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(4));
+               asn1_write_uint8(data, tree->u.extended.dnAttributes);
+               asn1_pop_tag(data);
                asn1_pop_tag(data);
                break;
-       }
+
        default:
-               return False;
+               return false;
        }
        return !data->has_error;
 }
@@ -368,56 +187,58 @@ static void ldap_encode_response(struct asn1_data *data, struct ldap_Result *res
        }
 }
 
-BOOL ldap_encode(struct ldap_message *msg, DATA_BLOB *result)
+bool ldap_encode(struct ldap_message *msg, DATA_BLOB *result, TALLOC_CTX *mem_ctx)
 {
-       struct asn1_data data;
+       struct asn1_data *data = asn1_init(mem_ctx);
        int i, j;
 
-       ZERO_STRUCT(data);
-       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-       asn1_write_Integer(&data, msg->messageid);
+       if (!data) return false;
+
+       asn1_push_tag(data, ASN1_SEQUENCE(0));
+       asn1_write_Integer(data, msg->messageid);
 
        switch (msg->type) {
        case LDAP_TAG_BindRequest: {
                struct ldap_BindRequest *r = &msg->r.BindRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               asn1_write_Integer(&data, r->version);
-               asn1_write_OctetString(&data, r->dn,
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_Integer(data, r->version);
+               asn1_write_OctetString(data, r->dn,
                                       (r->dn != NULL) ? strlen(r->dn) : 0);
 
                switch (r->mechanism) {
                case LDAP_AUTH_MECH_SIMPLE:
                        /* context, primitive */
-                       asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(0));
-                       asn1_write(&data, r->creds.password,
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
+                       asn1_write(data, r->creds.password,
                                   strlen(r->creds.password));
-                       asn1_pop_tag(&data);
+                       asn1_pop_tag(data);
                        break;
                case LDAP_AUTH_MECH_SASL:
                        /* context, constructed */
-                       asn1_push_tag(&data, ASN1_CONTEXT(3));
-                       asn1_write_OctetString(&data, r->creds.SASL.mechanism,
+                       asn1_push_tag(data, ASN1_CONTEXT(3));
+                       asn1_write_OctetString(data, r->creds.SASL.mechanism,
                                               strlen(r->creds.SASL.mechanism));
-                       asn1_write_OctetString(&data, r->creds.SASL.secblob.data,
-                                              r->creds.SASL.secblob.length);
-                       asn1_pop_tag(&data);
+                       if (r->creds.SASL.secblob) {
+                               asn1_write_OctetString(data, r->creds.SASL.secblob->data,
+                                                      r->creds.SASL.secblob->length);
+                       }
+                       asn1_pop_tag(data);
                        break;
                default:
-                       return False;
+                       return false;
                }
 
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_BindResponse: {
                struct ldap_BindResponse *r = &msg->r.BindResponse;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               ldap_encode_response(&data, &r->response);
-               if (r->SASL.secblob.length > 0) {
-                       asn1_write_ContextSimple(&data, 7, &r->SASL.secblob);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, &r->response);
+               if (r->SASL.secblob) {
+                       asn1_write_ContextSimple(data, 7, r->SASL.secblob);
                }
-               asn1_pop_tag(&data);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_UnbindRequest: {
@@ -426,256 +247,272 @@ BOOL ldap_encode(struct ldap_message *msg, DATA_BLOB *result)
        }
        case LDAP_TAG_SearchRequest: {
                struct ldap_SearchRequest *r = &msg->r.SearchRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               asn1_write_OctetString(&data, r->basedn, strlen(r->basedn));
-               asn1_write_enumerated(&data, r->scope);
-               asn1_write_enumerated(&data, r->deref);
-               asn1_write_Integer(&data, r->sizelimit);
-               asn1_write_Integer(&data, r->timelimit);
-               asn1_write_BOOLEAN(&data, r->attributesonly);
-
-               {
-                       TALLOC_CTX *mem_ctx = talloc_init("ldap_parse_tree");
-                       struct ldap_parse_tree *tree;
-
-                       if (mem_ctx == NULL)
-                               return False;
-
-                       tree = ldap_parse_tree(mem_ctx, r->filter);
-
-                       if (tree == NULL)
-                               return False;
-
-                       ldap_push_filter(&data, tree);
-
-                       talloc_destroy(mem_ctx);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->basedn, strlen(r->basedn));
+               asn1_write_enumerated(data, r->scope);
+               asn1_write_enumerated(data, r->deref);
+               asn1_write_Integer(data, r->sizelimit);
+               asn1_write_Integer(data, r->timelimit);
+               asn1_write_BOOLEAN(data, r->attributesonly);
+
+               if (!ldap_push_filter(data, r->tree)) {
+                       return false;
                }
 
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
                for (i=0; i<r->num_attributes; i++) {
-                       asn1_write_OctetString(&data, r->attributes[i],
+                       asn1_write_OctetString(data, r->attributes[i],
                                               strlen(r->attributes[i]));
                }
-               asn1_pop_tag(&data);
-
-               asn1_pop_tag(&data);
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_SearchResultEntry: {
                struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
                for (i=0; i<r->num_attributes; i++) {
-                       struct ldap_attribute *attr = &r->attributes[i];
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_OctetString(&data, attr->name,
+                       struct ldb_message_element *attr = &r->attributes[i];
+                       asn1_push_tag(data, ASN1_SEQUENCE(0));
+                       asn1_write_OctetString(data, attr->name,
                                               strlen(attr->name));
-                       asn1_push_tag(&data, ASN1_SEQUENCE(1));
+                       asn1_push_tag(data, ASN1_SEQUENCE(1));
                        for (j=0; j<attr->num_values; j++) {
-                               asn1_write_OctetString(&data,
+                               asn1_write_OctetString(data,
                                                       attr->values[j].data,
                                                       attr->values[j].length);
                        }
-                       asn1_pop_tag(&data);
-                       asn1_pop_tag(&data);
+                       asn1_pop_tag(data);
+                       asn1_pop_tag(data);
                }
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_SearchResultDone: {
                struct ldap_Result *r = &msg->r.SearchResultDone;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               ldap_encode_response(&data, r);
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_ModifyRequest: {
                struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
 
                for (i=0; i<r->num_mods; i++) {
-                       struct ldap_attribute *attrib = &r->mods[i].attrib;
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_enumerated(&data, r->mods[i].type);
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_OctetString(&data, attrib->name,
+                       struct ldb_message_element *attrib = &r->mods[i].attrib;
+                       asn1_push_tag(data, ASN1_SEQUENCE(0));
+                       asn1_write_enumerated(data, r->mods[i].type);
+                       asn1_push_tag(data, ASN1_SEQUENCE(0));
+                       asn1_write_OctetString(data, attrib->name,
                                               strlen(attrib->name));
-                       asn1_push_tag(&data, ASN1_SET);
+                       asn1_push_tag(data, ASN1_SET);
                        for (j=0; j<attrib->num_values; j++) {
-                               asn1_write_OctetString(&data,
+                               asn1_write_OctetString(data,
                                                       attrib->values[j].data,
                                                       attrib->values[j].length);
        
                        }
-                       asn1_pop_tag(&data);
-                       asn1_pop_tag(&data);
-                       asn1_pop_tag(&data);
+                       asn1_pop_tag(data);
+                       asn1_pop_tag(data);
+                       asn1_pop_tag(data);
                }
                
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_ModifyResponse: {
                struct ldap_Result *r = &msg->r.ModifyResponse;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               ldap_encode_response(&data, r);
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_AddRequest: {
                struct ldap_AddRequest *r = &msg->r.AddRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
 
                for (i=0; i<r->num_attributes; i++) {
-                       struct ldap_attribute *attrib = &r->attributes[i];
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_OctetString(&data, attrib->name,
+                       struct ldb_message_element *attrib = &r->attributes[i];
+                       asn1_push_tag(data, ASN1_SEQUENCE(0));
+                       asn1_write_OctetString(data, attrib->name,
                                               strlen(attrib->name));
-                       asn1_push_tag(&data, ASN1_SET);
+                       asn1_push_tag(data, ASN1_SET);
                        for (j=0; j<r->attributes[i].num_values; j++) {
-                               asn1_write_OctetString(&data,
+                               asn1_write_OctetString(data,
                                                       attrib->values[j].data,
                                                       attrib->values[j].length);
                        }
-                       asn1_pop_tag(&data);
-                       asn1_pop_tag(&data);
+                       asn1_pop_tag(data);
+                       asn1_pop_tag(data);
                }
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_AddResponse: {
                struct ldap_Result *r = &msg->r.AddResponse;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               ldap_encode_response(&data, r);
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_DelRequest: {
                struct ldap_DelRequest *r = &msg->r.DelRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION_SIMPLE(msg->type));
-               asn1_write(&data, r->dn, strlen(r->dn));
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION_SIMPLE(msg->type));
+               asn1_write(data, r->dn, strlen(r->dn));
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_DelResponse: {
                struct ldap_Result *r = &msg->r.DelResponse;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               ldap_encode_response(&data, r);
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_ModifyDNRequest: {
                struct ldap_ModifyDNRequest *r = &msg->r.ModifyDNRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_write_OctetString(&data, r->newrdn, strlen(r->newrdn));
-               asn1_write_BOOLEAN(&data, r->deleteolddn);
-               if (r->newsuperior != NULL) {
-                       asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(0));
-                       asn1_write(&data, r->newsuperior,
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_write_OctetString(data, r->newrdn, strlen(r->newrdn));
+               asn1_write_BOOLEAN(data, r->deleteolddn);
+               if (r->newsuperior) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
+                       asn1_write(data, r->newsuperior,
                                   strlen(r->newsuperior));
-                       asn1_pop_tag(&data);
+                       asn1_pop_tag(data);
                }
-               asn1_pop_tag(&data);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_ModifyDNResponse: {
                struct ldap_Result *r = &msg->r.ModifyDNResponse;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               ldap_encode_response(&data, r);
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_CompareRequest: {
                struct ldap_CompareRequest *r = &msg->r.CompareRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
-               asn1_write_OctetString(&data, r->attribute,
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
+               asn1_write_OctetString(data, r->attribute,
                                       strlen(r->attribute));
-               asn1_write_OctetString(&data, r->value.data,
+               asn1_write_OctetString(data, r->value.data,
                                       r->value.length);
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_CompareResponse: {
                struct ldap_Result *r = &msg->r.ModifyDNResponse;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               ldap_encode_response(&data, r);
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_AbandonRequest: {
                struct ldap_AbandonRequest *r = &msg->r.AbandonRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION_SIMPLE(msg->type));
-               asn1_write_implicit_Integer(&data, r->messageid);
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION_SIMPLE(msg->type));
+               asn1_write_implicit_Integer(data, r->messageid);
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_SearchResultReference: {
                struct ldap_SearchResRef *r = &msg->r.SearchResultReference;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               asn1_write_OctetString(&data, r->referral, strlen(r->referral));
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->referral, strlen(r->referral));
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_ExtendedRequest: {
                struct ldap_ExtendedRequest *r = &msg->r.ExtendedRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(0));
-               asn1_write(&data, r->oid, strlen(r->oid));
-               asn1_pop_tag(&data);
-               asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(1));
-               asn1_write(&data, r->value.data, r->value.length);
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
+               asn1_write(data, r->oid, strlen(r->oid));
+               asn1_pop_tag(data);
+               if (r->value) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(1));
+                       asn1_write(data, r->value->data, r->value->length);
+                       asn1_pop_tag(data);
+               }
+               asn1_pop_tag(data);
                break;
        }
        case LDAP_TAG_ExtendedResponse: {
                struct ldap_ExtendedResponse *r = &msg->r.ExtendedResponse;
-               asn1_push_tag(&data, ASN1_APPLICATION(msg->type));
-               ldap_encode_response(&data, &r->response);
-               asn1_pop_tag(&data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, &r->response);
+               if (r->oid) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(10));
+                       asn1_write(data, r->oid, strlen(r->oid));
+                       asn1_pop_tag(data);
+               }
+               if (r->value) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(11));
+                       asn1_write(data, r->value->data, r->value->length);
+                       asn1_pop_tag(data);
+               }
+               asn1_pop_tag(data);
                break;
        }
        default:
-               return False;
+               return false;
+       }
+
+       if (msg->controls != NULL) {
+               asn1_push_tag(data, ASN1_CONTEXT(0));
+               
+               for (i = 0; msg->controls[i] != NULL; i++) {
+                       if (!ldap_encode_control(mem_ctx, data, msg->controls[i])) {
+                               return false;
+                       }
+               }
+
+               asn1_pop_tag(data);
+       }
+
+       asn1_pop_tag(data);
+
+       if (data->has_error) {
+               asn1_free(data);
+               return false;
        }
 
-       asn1_pop_tag(&data);
-       *result = data_blob(data.data, data.length);
-       asn1_free(&data);
-       return True;
+       *result = data_blob_talloc(mem_ctx, data->data, data->length);
+       asn1_free(data);
+       return true;
 }
 
 static const char *blob2string_talloc(TALLOC_CTX *mem_ctx,
                                      DATA_BLOB blob)
 {
-       char *result = talloc_size(mem_ctx, blob.length+1);
+       char *result = talloc_array(mem_ctx, char, blob.length+1);
        memcpy(result, blob.data, blob.length);
        result[blob.length] = '\0';
        return result;
 }
 
-static BOOL asn1_read_OctetString_talloc(TALLOC_CTX *mem_ctx,
+static bool asn1_read_OctetString_talloc(TALLOC_CTX *mem_ctx,
                                         struct asn1_data *data,
                                         const char **result)
 {
        DATA_BLOB string;
-       if (!asn1_read_OctetString(data, &string))
-               return False;
+       if (!asn1_read_OctetString(data, mem_ctx, &string))
+               return false;
        *result = blob2string_talloc(mem_ctx, string);
        data_blob_free(&string);
-       return True;
+       return true;
 }
 
 static void ldap_decode_response(TALLOC_CTX *mem_ctx,
@@ -694,125 +531,379 @@ static void ldap_decode_response(TALLOC_CTX *mem_ctx,
        }
 }
 
-static BOOL ldap_decode_filter(TALLOC_CTX *mem_ctx, struct asn1_data *data,
-                              char **filter)
+static struct ldb_val **ldap_decode_substring(TALLOC_CTX *mem_ctx, struct ldb_val **chunks, int chunk_num, char *value)
+{
+
+       chunks = talloc_realloc(mem_ctx, chunks, struct ldb_val *, chunk_num + 2);
+       if (chunks == NULL) {
+               return NULL;
+       }
+
+       chunks[chunk_num] = talloc(mem_ctx, struct ldb_val);
+       if (chunks[chunk_num] == NULL) {
+               return NULL;
+       }
+
+       chunks[chunk_num]->data = (uint8_t *)talloc_strdup(mem_ctx, value);
+       if (chunks[chunk_num]->data == NULL) {
+               return NULL;
+       }
+       chunks[chunk_num]->length = strlen(value);
+
+       chunks[chunk_num + 1] = '\0';
+
+       return chunks;
+}
+
+
+/*
+  parse the ASN.1 formatted search string into a ldb_parse_tree
+*/
+static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx, 
+                                                     struct asn1_data *data)
 {
-       uint8 filter_tag, tag_desc;
+       uint8_t filter_tag;
+       struct ldb_parse_tree *ret;
 
-       if (!asn1_peek_uint8(data, &filter_tag))
-               return False;
+       if (!asn1_peek_uint8(data, &filter_tag)) {
+               return NULL;
+       }
 
-       tag_desc = filter_tag;
        filter_tag &= 0x1f;     /* strip off the asn1 stuff */
-       tag_desc &= 0xe0;
+
+       ret = talloc(mem_ctx, struct ldb_parse_tree);
+       if (ret == NULL) return NULL;
 
        switch(filter_tag) {
-       case 0: {
-               /* AND of one or more filters */
-               if (tag_desc != 0xa0) /* context compount */
-                       return False;
+       case 0:
+       case 1:
+               /* AND or OR of one or more filters */
+               ret->operation = (filter_tag == 0)?LDB_OP_AND:LDB_OP_OR;
+               ret->u.list.num_elements = 0;
+               ret->u.list.elements = NULL;
+
+               if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
+                       goto failed;
+               }
 
-               asn1_start_tag(data, ASN1_CONTEXT(0));
+               while (asn1_tag_remaining(data) > 0) {
+                       struct ldb_parse_tree *subtree;
+                       subtree = ldap_decode_filter_tree(ret, data);
+                       if (subtree == NULL) {
+                               goto failed;
+                       }
+                       ret->u.list.elements = 
+                               talloc_realloc(ret, ret->u.list.elements, 
+                                              struct ldb_parse_tree *, 
+                                              ret->u.list.num_elements+1);
+                       if (ret->u.list.elements == NULL) {
+                               goto failed;
+                       }
+                       talloc_steal(ret->u.list.elements, subtree);
+                       ret->u.list.elements[ret->u.list.num_elements] = subtree;
+                       ret->u.list.num_elements++;
+               }
+               if (!asn1_end_tag(data)) {
+                       goto failed;
+               }
+               break;
 
-               *filter = talloc_strdup(mem_ctx, "(&");
-               if (*filter == NULL)
-                       return False;
+       case 2:
+               /* 'not' operation */
+               if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
+                       goto failed;
+               }
 
-               while (asn1_tag_remaining(data) > 0) {
-                       char *subfilter;
-                       if (!ldap_decode_filter(mem_ctx, data, &subfilter))
-                               return False;
-                       *filter = talloc_asprintf(mem_ctx, "%s%s", *filter,
-                                                 subfilter);
-                       if (*filter == NULL)
-                               return False;
+               ret->operation = LDB_OP_NOT;
+               ret->u.isnot.child = ldap_decode_filter_tree(ret, data);
+               if (ret->u.isnot.child == NULL) {
+                       goto failed;
+               }
+               if (!asn1_end_tag(data)) {
+                       goto failed;
                }
+               break;
+
+       case 3: {
+               /* equalityMatch */
+               const char *attrib;
+               DATA_BLOB value;
+
+               asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
+               asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
+               asn1_read_OctetString(data, mem_ctx, &value);
                asn1_end_tag(data);
+               if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
+                       goto failed;
+               }
 
-               *filter = talloc_asprintf(mem_ctx, "%s)", *filter);
+               ret->operation = LDB_OP_EQUALITY;
+               ret->u.equality.attr = talloc_steal(ret, attrib);
+               ret->u.equality.value.data = talloc_steal(ret, value.data);
+               ret->u.equality.value.length = value.length;
                break;
        }
-       case 1: {
-               /* OR of one or more filters */
-               if (tag_desc != 0xa0) /* context compount */
-                       return False;
+       case 4: {
+               /* substrings */
+               DATA_BLOB attr;
+               uint8_t subs_tag;
+               char *value;
+               int chunk_num = 0;
 
-               asn1_start_tag(data, ASN1_CONTEXT(1));
+               if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
+                       goto failed;
+               }
+               if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
+                       goto failed;
+               }
 
-               *filter = talloc_strdup(mem_ctx, "(|");
-               if (*filter == NULL)
-                       return False;
+               ret->operation = LDB_OP_SUBSTRING;
+               ret->u.substring.attr = talloc_strndup(ret, (char *)attr.data, attr.length);
+               ret->u.substring.chunks = NULL;
+               ret->u.substring.start_with_wildcard = 1;
+               ret->u.substring.end_with_wildcard = 1;
 
-               while (asn1_tag_remaining(data) > 0) {
-                       char *subfilter;
-                       if (!ldap_decode_filter(mem_ctx, data, &subfilter))
-                               return False;
-                       *filter = talloc_asprintf(mem_ctx, "%s%s", *filter,
-                                                 subfilter);
-                       if (*filter == NULL)
-                               return False;
+               if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+                       goto failed;
                }
 
+               while (asn1_tag_remaining(data)) {
+                       asn1_peek_uint8(data, &subs_tag);
+                       subs_tag &= 0x1f;       /* strip off the asn1 stuff */
+                       if (subs_tag > 2) goto failed;
+
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(subs_tag));
+                       asn1_read_LDAPString(data, mem_ctx, &value);
+                       asn1_end_tag(data);
+
+                       switch (subs_tag) {
+                       case 0:
+                               if (ret->u.substring.chunks != NULL) {
+                                       /* initial value found in the middle */
+                                       goto failed;
+                               }
+
+                               ret->u.substring.chunks = ldap_decode_substring(ret, NULL, 0, value);
+                               if (ret->u.substring.chunks == NULL) {
+                                       goto failed;
+                               }
+
+                               ret->u.substring.start_with_wildcard = 0;
+                               chunk_num = 1;
+                               break;
+
+                       case 1:
+                               if (ret->u.substring.end_with_wildcard == 0) {
+                                       /* "any" value found after a "final" value */
+                                       goto failed;
+                               }
+
+                               ret->u.substring.chunks = ldap_decode_substring(ret,
+                                                                               ret->u.substring.chunks,
+                                                                               chunk_num,
+                                                                               value);
+                               if (ret->u.substring.chunks == NULL) {
+                                       goto failed;
+                               }
+
+                               chunk_num++;
+                               break;
+
+                       case 2:
+                               ret->u.substring.chunks = ldap_decode_substring(ret,
+                                                                               ret->u.substring.chunks,
+                                                                               chunk_num,
+                                                                               value);
+                               if (ret->u.substring.chunks == NULL) {
+                                       goto failed;
+                               }
+
+                               ret->u.substring.end_with_wildcard = 0;
+                               break;
+
+                       default:
+                               goto failed;
+                       }
+
+               }
+
+               if (!asn1_end_tag(data)) { /* SEQUENCE */
+                       goto failed;
+               }
+
+               if (!asn1_end_tag(data)) {
+                       goto failed;
+               }
+               break;
+       }
+       case 5: {
+               /* greaterOrEqual */
+               const char *attrib;
+               DATA_BLOB value;
+
+               asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
+               asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
+               asn1_read_OctetString(data, mem_ctx, &value);
                asn1_end_tag(data);
+               if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
+                       goto failed;
+               }
 
-               *filter = talloc_asprintf(mem_ctx, "%s)", *filter);
+               ret->operation = LDB_OP_GREATER;
+               ret->u.comparison.attr = talloc_steal(ret, attrib);
+               ret->u.comparison.value.data = talloc_steal(ret, value.data);
+               ret->u.comparison.value.length = value.length;
                break;
        }
-       case 3: {
-               /* equalityMatch */
-               const char *attrib, *value;
-               if (tag_desc != 0xa0) /* context compound */
-                       return False;
-               asn1_start_tag(data, ASN1_CONTEXT(3));
+       case 6: {
+               /* lessOrEqual */
+               const char *attrib;
+               DATA_BLOB value;
+
+               asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
                asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
-               asn1_read_OctetString_talloc(mem_ctx, data, &value);
+               asn1_read_OctetString(data, mem_ctx, &value);
                asn1_end_tag(data);
-               if ((data->has_error) || (attrib == NULL) || (value == NULL))
-                       return False;
-               *filter = talloc_asprintf(mem_ctx, "(%s=%s)", attrib, value);
+               if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
+                       goto failed;
+               }
+
+               ret->operation = LDB_OP_LESS;
+               ret->u.comparison.attr = talloc_steal(ret, attrib);
+               ret->u.comparison.value.data = talloc_steal(ret, value.data);
+               ret->u.comparison.value.length = value.length;
                break;
        }
        case 7: {
                /* Normal presence, "attribute=*" */
-               int attr_len;
-               char *attr_name;
-               if (tag_desc != 0x80) /* context simple */
-                       return False;
-               if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(7)))
-                       return False;
-               attr_len = asn1_tag_remaining(data);
-               attr_name = malloc(attr_len+1);
-               if (attr_name == NULL)
-                       return False;
-               asn1_read(data, attr_name, attr_len);
-               attr_name[attr_len] = '\0';
-               *filter = talloc_asprintf(mem_ctx, "(%s=*)", attr_name);
-               SAFE_FREE(attr_name);
+               char *attr;
+
+               if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(filter_tag))) {
+                       goto failed;
+               }
+               if (!asn1_read_LDAPString(data, ret, &attr)) {
+                       goto failed;
+               }
+
+               ret->operation = LDB_OP_PRESENT;
+               ret->u.present.attr = talloc_steal(ret, attr);
+
+               if (!asn1_end_tag(data)) {
+                       goto failed;
+               }
+               break;
+       }
+       case 8: {
+               /* approx */
+               const char *attrib;
+               DATA_BLOB value;
+
+               asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
+               asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
+               asn1_read_OctetString(data, mem_ctx, &value);
+               asn1_end_tag(data);
+               if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
+                       goto failed;
+               }
+
+               ret->operation = LDB_OP_APPROX;
+               ret->u.comparison.attr = talloc_steal(ret, attrib);
+               ret->u.comparison.value.data = talloc_steal(ret, value.data);
+               ret->u.comparison.value.length = value.length;
+               break;
+       }
+       case 9: {
+               char *oid = NULL, *attr = NULL, *value;
+               uint8_t dnAttributes;
+               /* an extended search */
+               if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
+                       goto failed;
+               }
+
+               /* FIXME: read carefully rfc2251.txt there are a number of 'MUST's
+                  we need to check we properly implement --SSS */ 
+               /* either oid or type must be defined */
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(1))) { /* optional */
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(1));
+                       asn1_read_LDAPString(data, ret, &oid);
+                       asn1_end_tag(data);
+               }
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(2))) {      /* optional  */
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(2));
+                       asn1_read_LDAPString(data, ret, &attr);
+                       asn1_end_tag(data);
+               }
+               asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(3));
+               asn1_read_LDAPString(data, ret, &value);
                asn1_end_tag(data);
+               /* dnAttributes is marked as BOOLEAN DEFAULT FALSE
+                  it is not marked as OPTIONAL but openldap tools
+                  do not set this unless it is to be set as TRUE
+                  NOTE: openldap tools do not work with AD as it
+                  seems that AD always requires the dnAttributes
+                  boolean value to be set */
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(4))) {
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(4));
+                       asn1_read_uint8(data, &dnAttributes);
+                       asn1_end_tag(data);
+               } else {
+                       dnAttributes = 0;
+               }
+               if ((oid == NULL && attr == NULL) || (value == NULL)) {
+                       goto failed;
+               }
+
+               if (oid) {
+                       ret->operation               = LDB_OP_EXTENDED;
+
+                       /* From the RFC2251: If the type field is
+                          absent and matchingRule is present, the matchValue is compared
+                          against all attributes in an entry which support that matchingRule
+                       */
+                       if (attr) {
+                               ret->u.extended.attr = talloc_steal(ret, attr);
+                       } else {
+                               ret->u.extended.attr = talloc_strdup(ret, "*");
+                       }
+                       ret->u.extended.rule_id      = talloc_steal(ret, oid);
+                       ret->u.extended.value.data   = talloc_steal(ret, value);
+                       ret->u.extended.value.length = strlen(value);
+                       ret->u.extended.dnAttributes = dnAttributes;
+               } else {
+                       ret->operation               = LDB_OP_EQUALITY;
+                       ret->u.equality.attr         = talloc_steal(ret, attr);
+                       ret->u.equality.value.data   = talloc_steal(ret, value);
+                       ret->u.equality.value.length = strlen(value);
+               }
+               if (!asn1_end_tag(data)) {
+                       goto failed;
+               }
                break;
        }
+
        default:
-               return False;
+               DEBUG(0,("Unsupported LDAP filter operation 0x%x\n", filter_tag));
+               goto failed;
        }
-       if (*filter == NULL)
-               return False;
-       return True;
+       
+       return ret;
+
+failed:
+       talloc_free(ret);
+       return NULL;    
 }
 
+
 static void ldap_decode_attrib(TALLOC_CTX *mem_ctx, struct asn1_data *data,
-                              struct ldap_attribute *attrib)
+                              struct ldb_message_element *attrib)
 {
        asn1_start_tag(data, ASN1_SEQUENCE(0));
        asn1_read_OctetString_talloc(mem_ctx, data, &attrib->name);
        asn1_start_tag(data, ASN1_SET);
        while (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
                DATA_BLOB blob;
-               struct ldap_val value;
-               asn1_read_OctetString(data, &blob);
-               value.data = blob.data;
-               value.length = blob.length;
-               add_value_to_attrib(mem_ctx, &value, attrib);
-               data_blob_free(&blob);
+               asn1_read_OctetString(data, mem_ctx, &blob);
+               add_value_to_attrib(mem_ctx, &blob, attrib);
        }
        asn1_end_tag(data);
        asn1_end_tag(data);
@@ -820,12 +911,12 @@ static void ldap_decode_attrib(TALLOC_CTX *mem_ctx, struct asn1_data *data,
 }
 
 static void ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data,
-                               struct ldap_attribute **attributes,
+                               struct ldb_message_element **attributes,
                                int *num_attributes)
 {
        asn1_start_tag(data, ASN1_SEQUENCE(0));
        while (asn1_peek_tag(data, ASN1_SEQUENCE(0))) {
-               struct ldap_attribute attrib;
+               struct ldb_message_element attrib;
                ZERO_STRUCT(attrib);
                ldap_decode_attrib(mem_ctx, data, &attrib);
                add_attrib_to_array_talloc(mem_ctx, &attrib,
@@ -834,15 +925,17 @@ static void ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data,
        asn1_end_tag(data);
 }
 
-BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
+/* This routine returns LDAP status codes */
+
+NTSTATUS ldap_decode(struct asn1_data *data, struct ldap_message *msg)
 {
-       uint8 tag;
+       uint8_t tag;
 
        asn1_start_tag(data, ASN1_SEQUENCE(0));
        asn1_read_Integer(data, &msg->messageid);
 
        if (!asn1_peek_uint8(data, &tag))
-               return False;
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
 
        switch(tag) {
 
@@ -851,15 +944,21 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                msg->type = LDAP_TAG_BindRequest;
                asn1_start_tag(data, tag);
                asn1_read_Integer(data, &r->version);
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
                if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(0))) {
                        int pwlen;
                        r->creds.password = "";
                        r->mechanism = LDAP_AUTH_MECH_SIMPLE;
                        asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(0));
                        pwlen = asn1_tag_remaining(data);
+                       if (pwlen == -1) {
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+                       }
                        if (pwlen != 0) {
-                               char *pw = talloc_size(msg->mem_ctx, pwlen+1);
+                               char *pw = talloc_array(msg, char, pwlen+1);
+                               if (!pw) {
+                                       return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                               }
                                asn1_read(data, pw, pwlen);
                                pw[pwlen] = '\0';
                                r->creds.password = pw;
@@ -868,12 +967,24 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                } else if (asn1_peek_tag(data, ASN1_CONTEXT(3))){
                        asn1_start_tag(data, ASN1_CONTEXT(3));
                        r->mechanism = LDAP_AUTH_MECH_SASL;
-                       asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->creds.SASL.mechanism);
-                       asn1_read_OctetString(data, &r->creds.SASL.secblob);
-                       if (r->creds.SASL.secblob.data) {
-                               talloc_steal(msg->mem_ctx, r->creds.SASL.secblob.data);
+                       asn1_read_OctetString_talloc(msg, data, &r->creds.SASL.mechanism);
+                       if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { /* optional */
+                               DATA_BLOB tmp_blob = data_blob(NULL, 0);
+                               asn1_read_OctetString(data, msg, &tmp_blob);
+                               r->creds.SASL.secblob = talloc(msg, DATA_BLOB);
+                               if (!r->creds.SASL.secblob) {
+                                       return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                               }
+                               *r->creds.SASL.secblob = data_blob_talloc(r->creds.SASL.secblob,
+                                                                         tmp_blob.data, tmp_blob.length);
+                               data_blob_free(&tmp_blob);
+                       } else {
+                               r->creds.SASL.secblob = NULL;
                        }
                        asn1_end_tag(data);
+               } else {
+                       /* Neither Simple nor SASL bind */
+                       return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
                }
                asn1_end_tag(data);
                break;
@@ -883,14 +994,19 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_BindResponse *r = &msg->r.BindResponse;
                msg->type = LDAP_TAG_BindResponse;
                asn1_start_tag(data, tag);
-               ldap_decode_response(msg->mem_ctx, data, &r->response);
+               ldap_decode_response(msg, data, &r->response);
                if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(7))) {
                        DATA_BLOB tmp_blob = data_blob(NULL, 0);
                        asn1_read_ContextSimple(data, 7, &tmp_blob);
-                       r->SASL.secblob = data_blob_talloc(msg->mem_ctx, tmp_blob.data, tmp_blob.length);
+                       r->SASL.secblob = talloc(msg, DATA_BLOB);
+                       if (!r->SASL.secblob) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+                       *r->SASL.secblob = data_blob_talloc(r->SASL.secblob,
+                                                           tmp_blob.data, tmp_blob.length);
                        data_blob_free(&tmp_blob);
                } else {
-                       r->SASL.secblob = data_blob(NULL, 0);
+                       r->SASL.secblob = NULL;
                }
                asn1_end_tag(data);
                break;
@@ -907,31 +1023,33 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_SearchRequest *r = &msg->r.SearchRequest;
                msg->type = LDAP_TAG_SearchRequest;
                asn1_start_tag(data, tag);
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->basedn);
+               asn1_read_OctetString_talloc(msg, data, &r->basedn);
                asn1_read_enumerated(data, (int *)&(r->scope));
                asn1_read_enumerated(data, (int *)&(r->deref));
                asn1_read_Integer(data, &r->sizelimit);
                asn1_read_Integer(data, &r->timelimit);
                asn1_read_BOOLEAN(data, &r->attributesonly);
 
-               /* Maybe create a TALLOC_CTX for the filter? This can waste
-                * quite a bit of memory recursing down. */
-               ldap_decode_filter(msg->mem_ctx, data, &r->filter);
+               r->tree = ldap_decode_filter_tree(msg, data);
+               if (r->tree == NULL) {
+                       return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+               }
 
                asn1_start_tag(data, ASN1_SEQUENCE(0));
 
                r->num_attributes = 0;
                r->attributes = NULL;
 
-               while (asn1_tag_remaining(data) > 0) {
+               while (asn1_tag_remaining(data) > 0) {                                  
+
                        const char *attr;
-                       if (!asn1_read_OctetString_talloc(msg->mem_ctx, data,
+                       if (!asn1_read_OctetString_talloc(msg, data,
                                                          &attr))
-                               return False;
-                       if (!add_string_to_array(msg->mem_ctx, attr,
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+                       if (!add_string_to_array(msg, attr,
                                                 &r->attributes,
                                                 &r->num_attributes))
-                               return False;
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
                }
 
                asn1_end_tag(data);
@@ -945,8 +1063,8 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                r->attributes = NULL;
                r->num_attributes = 0;
                asn1_start_tag(data, tag);
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
-               ldap_decode_attribs(msg->mem_ctx, data, &r->attributes,
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
+               ldap_decode_attribs(msg, data, &r->attributes,
                                    &r->num_attributes);
                asn1_end_tag(data);
                break;
@@ -956,7 +1074,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_Result *r = &msg->r.SearchResultDone;
                msg->type = LDAP_TAG_SearchResultDone;
                asn1_start_tag(data, tag);
-               ldap_decode_response(msg->mem_ctx, data, r);
+               ldap_decode_response(msg, data, r);
                asn1_end_tag(data);
                break;
        }
@@ -965,7 +1083,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_SearchResRef *r = &msg->r.SearchResultReference;
                msg->type = LDAP_TAG_SearchResultReference;
                asn1_start_tag(data, tag);
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->referral);
+               asn1_read_OctetString_talloc(msg, data, &r->referral);
                asn1_end_tag(data);
                break;
        }
@@ -974,7 +1092,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
                msg->type = LDAP_TAG_ModifyRequest;
                asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_ModifyRequest));
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
                asn1_start_tag(data, ASN1_SEQUENCE(0));
 
                r->num_mods = 0;
@@ -987,11 +1105,12 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                        asn1_start_tag(data, ASN1_SEQUENCE(0));
                        asn1_read_enumerated(data, &v);
                        mod.type = v;
-                       ldap_decode_attrib(msg->mem_ctx, data, &mod.attrib);
+                       ldap_decode_attrib(msg, data, &mod.attrib);
                        asn1_end_tag(data);
-                       if (!add_mod_to_array_talloc(msg->mem_ctx, &mod,
-                                                    &r->mods, &r->num_mods))
-                               break;
+                       if (!add_mod_to_array_talloc(msg, &mod,
+                                                    &r->mods, &r->num_mods)) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
                }
 
                asn1_end_tag(data);
@@ -1003,7 +1122,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_Result *r = &msg->r.ModifyResponse;
                msg->type = LDAP_TAG_ModifyResponse;
                asn1_start_tag(data, tag);
-               ldap_decode_response(msg->mem_ctx, data, r);
+               ldap_decode_response(msg, data, r);
                asn1_end_tag(data);
                break;
        }
@@ -1012,11 +1131,11 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_AddRequest *r = &msg->r.AddRequest;
                msg->type = LDAP_TAG_AddRequest;
                asn1_start_tag(data, tag);
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
 
                r->attributes = NULL;
                r->num_attributes = 0;
-               ldap_decode_attribs(msg->mem_ctx, data, &r->attributes,
+               ldap_decode_attribs(msg, data, &r->attributes,
                                    &r->num_attributes);
 
                asn1_end_tag(data);
@@ -1027,7 +1146,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_Result *r = &msg->r.AddResponse;
                msg->type = LDAP_TAG_AddResponse;
                asn1_start_tag(data, tag);
-               ldap_decode_response(msg->mem_ctx, data, r);
+               ldap_decode_response(msg, data, r);
                asn1_end_tag(data);
                break;
        }
@@ -1040,7 +1159,10 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                asn1_start_tag(data,
                               ASN1_APPLICATION_SIMPLE(LDAP_TAG_DelRequest));
                len = asn1_tag_remaining(data);
-               dn = talloc_size(msg->mem_ctx, len+1);
+               if (len == -1) {
+                       return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+               }
+               dn = talloc_array(msg, char, len+1);
                if (dn == NULL)
                        break;
                asn1_read(data, dn, len);
@@ -1054,7 +1176,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_Result *r = &msg->r.DelResponse;
                msg->type = LDAP_TAG_DelResponse;
                asn1_start_tag(data, tag);
-               ldap_decode_response(msg->mem_ctx, data, r);
+               ldap_decode_response(msg, data, r);
                asn1_end_tag(data);
                break;
        }
@@ -1064,8 +1186,8 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                msg->type = LDAP_TAG_ModifyDNRequest;
                asn1_start_tag(data,
                               ASN1_APPLICATION(LDAP_TAG_ModifyDNRequest));
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->newrdn);
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
+               asn1_read_OctetString_talloc(msg, data, &r->newrdn);
                asn1_read_BOOLEAN(data, &r->deleteolddn);
                r->newsuperior = NULL;
                if (asn1_tag_remaining(data) > 0) {
@@ -1073,9 +1195,13 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                        char *newsup;
                        asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(0));
                        len = asn1_tag_remaining(data);
-                       newsup = talloc_size(msg->mem_ctx, len+1);
-                       if (newsup == NULL)
-                               break;
+                       if (len == -1) {
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+                       }
+                       newsup = talloc_array(msg, char, len+1);
+                       if (newsup == NULL) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
                        asn1_read(data, newsup, len);
                        newsup[len] = '\0';
                        r->newsuperior = newsup;
@@ -1089,7 +1215,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_Result *r = &msg->r.ModifyDNResponse;
                msg->type = LDAP_TAG_ModifyDNResponse;
                asn1_start_tag(data, tag);
-               ldap_decode_response(msg->mem_ctx, data, r);
+               ldap_decode_response(msg, data, r);
                asn1_end_tag(data);
                break;
        }
@@ -1099,12 +1225,12 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                msg->type = LDAP_TAG_CompareRequest;
                asn1_start_tag(data,
                               ASN1_APPLICATION(LDAP_TAG_CompareRequest));
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
                asn1_start_tag(data, ASN1_SEQUENCE(0));
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->attribute);
-               asn1_read_OctetString(data, &r->value);
+               asn1_read_OctetString_talloc(msg, data, &r->attribute);
+               asn1_read_OctetString(data, msg, &r->value);
                if (r->value.data) {
-                       talloc_steal(msg->mem_ctx, r->value.data);
+                       talloc_steal(msg, r->value.data);
                }
                asn1_end_tag(data);
                asn1_end_tag(data);
@@ -1115,7 +1241,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                struct ldap_Result *r = &msg->r.CompareResponse;
                msg->type = LDAP_TAG_CompareResponse;
                asn1_start_tag(data, tag);
-               ldap_decode_response(msg->mem_ctx, data, r);
+               ldap_decode_response(msg, data, r);
                asn1_end_tag(data);
                break;
        }
@@ -1136,20 +1262,24 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                msg->type = LDAP_TAG_ExtendedRequest;
                asn1_start_tag(data,tag);
                if (!asn1_read_ContextSimple(data, 0, &tmp_blob)) {
-                       return False;
+                       return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
                }
-               r->oid = blob2string_talloc(msg->mem_ctx, tmp_blob);
+               r->oid = blob2string_talloc(msg, tmp_blob);
                data_blob_free(&tmp_blob);
                if (!r->oid) {
-                       return False;
+                       return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
                }
 
                if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(1))) {
                        asn1_read_ContextSimple(data, 1, &tmp_blob);
-                       r->value = data_blob_talloc(msg->mem_ctx, tmp_blob.data, tmp_blob.length);
+                       r->value = talloc(msg, DATA_BLOB);
+                       if (!r->value) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+                       *r->value = data_blob_talloc(r->value, tmp_blob.data, tmp_blob.length);
                        data_blob_free(&tmp_blob);
                } else {
-                       r->value = data_blob(NULL, 0);
+                       r->value = NULL;
                }
 
                asn1_end_tag(data);
@@ -1158,104 +1288,113 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
 
        case ASN1_APPLICATION(LDAP_TAG_ExtendedResponse): {
                struct ldap_ExtendedResponse *r = &msg->r.ExtendedResponse;
+               DATA_BLOB tmp_blob = data_blob(NULL, 0);
+
                msg->type = LDAP_TAG_ExtendedResponse;
                asn1_start_tag(data, tag);              
-               ldap_decode_response(msg->mem_ctx, data, &r->response);
-               /* I have to come across an operation that actually sends
-                * something back to really see what's going on. The currently
-                * needed pwdchange does not send anything back. */
-               r->name = NULL;
-               r->value.data = NULL;
-               r->value.length = 0;
+               ldap_decode_response(msg, data, &r->response);
+
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(10))) {
+                       asn1_read_ContextSimple(data, 1, &tmp_blob);
+                       r->oid = blob2string_talloc(msg, tmp_blob);
+                       data_blob_free(&tmp_blob);
+                       if (!r->oid) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+               } else {
+                       r->oid = NULL;
+               }
+
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(11))) {
+                       asn1_read_ContextSimple(data, 1, &tmp_blob);
+                       r->value = talloc(msg, DATA_BLOB);
+                       if (!r->value) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+                       *r->value = data_blob_talloc(r->value, tmp_blob.data, tmp_blob.length);
+                       data_blob_free(&tmp_blob);
+               } else {
+                       r->value = NULL;
+               }
+
                asn1_end_tag(data);
                break;
        }
        default: 
-               return False;
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
        }
 
-       msg->num_controls = 0;
        msg->controls = NULL;
+       msg->controls_decoded = NULL;
 
        if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
-               int i;
-               struct ldap_Control *ctrl = NULL;
+               int i = 0;
+               struct ldb_control **ctrl = NULL;
+               bool *decoded = NULL;
 
                asn1_start_tag(data, ASN1_CONTEXT(0));
 
-               for (i=0; asn1_peek_tag(data, ASN1_SEQUENCE(0)); i++) {
-                       asn1_start_tag(data, ASN1_SEQUENCE(0));
+               while (asn1_peek_tag(data, ASN1_SEQUENCE(0))) {
+                       DATA_BLOB value;
+                       /* asn1_start_tag(data, ASN1_SEQUENCE(0)); */
 
-                       ctrl = talloc_realloc_p(msg->mem_ctx, ctrl, struct ldap_Control, i+1);
+                       ctrl = talloc_realloc(msg, ctrl, struct ldb_control *, i+2);
                        if (!ctrl) {
-                               return False;
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
                        }
-                       ctrl[i].oid = NULL;
-                       ctrl[i].critical = False;
-                       ctrl[i].value = data_blob(NULL, 0);
 
-                       asn1_read_OctetString_talloc(ctrl, data, &ctrl[i].oid);
+                       decoded = talloc_realloc(msg, decoded, bool, i+1);
+                       if (!decoded) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
 
-                       if (asn1_peek_tag(data, ASN1_BOOLEAN)) {
-                               asn1_read_BOOLEAN(data, &ctrl[i].critical);
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       if (!ctrl[i]) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
                        }
 
-                       if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
-                               asn1_read_OctetString(data, &ctrl[i].value);
-                               if (ctrl[i].value.data) {
-                                       talloc_steal(msg->mem_ctx, ctrl[i].value.data);
+                       if (!ldap_decode_control_wrapper(ctrl, data, ctrl[i], &value)) {
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+                       }
+                       
+                       if (!ldap_decode_control_value(ctrl, value, ctrl[i])) {
+                               if (ctrl[i]->critical) {
+                                       ctrl[i]->data = NULL;
+                                       decoded[i] = false;
+                                       i++;
+                               } else {
+                                       talloc_free(ctrl[i]);
+                                       ctrl[i] = NULL;
                                }
+                       } else {
+                               decoded[i] = true;
+                               i++;
                        }
+               }
 
-                       asn1_end_tag(data);
+               if (ctrl != NULL) {
+                       ctrl[i] = NULL;
                }
-               msg->num_controls = i;
+
                msg->controls = ctrl;
+               msg->controls_decoded = decoded;
 
                asn1_end_tag(data);
        }
 
        asn1_end_tag(data);
-       return ((!data->has_error) && (data->nesting == NULL));
-}
-
-BOOL ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url,
-                         char **host, uint16 *port, BOOL *ldaps)
-{
-       int tmp_port = 0;
-       char protocol[11];
-       char tmp_host[255];
-       const char *p = url;
-       int ret;
-
-       /* skip leading "URL:" (if any) */
-       if (strncasecmp( p, "URL:", 4) == 0) {
-               p += 4;
+       if ((data->has_error) || (data->nesting != NULL)) {
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
        }
+       return NT_STATUS_OK;
+}
 
-       /* Paranoia check */
-       SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254);
-               
-       ret = sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port);
-       if (ret < 2) {
-               return False;
-       }
-
-       if (strequal(protocol, "ldap")) {
-               *port = 389;
-               *ldaps = False;
-       } else if (strequal(protocol, "ldaps")) {
-               *port = 636;
-               *ldaps = True;
-       } else {
-               DEBUG(0, ("unrecognised protocol (%s)!\n", protocol));
-               return False;
-       }
-
-       if (tmp_port != 0)
-               *port = tmp_port;
-
-       *host = talloc_strdup(mem_ctx, tmp_host);
 
-       return (*host != NULL);
+/*
+  return NT_STATUS_OK if a blob has enough bytes in it to be a full
+  ldap packet. Set packet_size if true.
+*/
+NTSTATUS ldap_full_packet(void *private_data, DATA_BLOB blob, size_t *packet_size)
+{
+       return asn1_full_tag(blob, ASN1_SEQUENCE(0), packet_size);
 }