r26613: Add a function to write a DATA_BLOB into an LDAPString.
[samba.git] / source4 / libcli / ldap / ldap.c
index c699820cea33f117cc1a9460cac7363071091226..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 "libcli/util/asn_1.h"
 #include "libcli/ldap/ldap.h"
 
 
-static BOOL ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree)
+static bool ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree)
 {
        int i;
 
@@ -39,7 +37,7 @@ static BOOL ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree
                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 false;
                        }
                }
                asn1_pop_tag(data);
@@ -48,7 +46,7 @@ static BOOL ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree
        case LDB_OP_NOT:
                asn1_push_tag(data, ASN1_CONTEXT(2));
                if (!ldap_push_filter(data, tree->u.isnot.child)) {
-                       return False;
+                       return false;
                }
                asn1_pop_tag(data);
                break;
@@ -79,7 +77,7 @@ static BOOL ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree
                i = 0;
                if ( ! tree->u.substring.start_with_wildcard) {
                        asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
-                       asn1_write_LDAPString(data, (char *)tree->u.substring.chunks[i]->data);
+                       asn1_write_DATA_BLOB_LDAPString(data, tree->u.substring.chunks[i]);
                        asn1_pop_tag(data);
                        i++;
                }
@@ -93,7 +91,7 @@ static BOOL ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree
                                ctx = 1;
                        }
                        asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(ctx));
-                       asn1_write_LDAPString(data, (char *)tree->u.substring.chunks[i]->data);
+                       asn1_write_DATA_BLOB_LDAPString(data, tree->u.substring.chunks[i]);
                        asn1_pop_tag(data);
                        i++;
                }
@@ -159,7 +157,7 @@ static BOOL ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree
                        asn1_pop_tag(data);
                }
                asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(3));
-               asn1_write_LDAPString(data, (char *)tree->u.extended.value.data);
+               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);
@@ -168,7 +166,7 @@ static BOOL ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree
                break;
 
        default:
-               return False;
+               return false;
        }
        return !data->has_error;
 }
@@ -189,53 +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, TALLOC_CTX *mem_ctx)
+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);
                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);
-               asn1_write_ContextSimple(&data, 7, &r->SASL.secblob);
-               asn1_pop_tag(&data);
+               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);
                break;
        }
        case LDAP_TAG_UnbindRequest: {
@@ -244,248 +247,272 @@ BOOL ldap_encode(struct ldap_message *msg, DATA_BLOB *result, TALLOC_CTX *mem_ct
        }
        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);
-
-               if (!ldap_push_filter(&data, r->tree)) {
-                       return False;
+               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 ldb_message_element *attr = &r->attributes[i];
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_OctetString(&data, attr->name,
+                       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 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,
+                       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 ldb_message_element *attrib = &r->attributes[i];
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_OctetString(&data, attrib->name,
+                       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);
+       asn1_pop_tag(data);
 
-       if (data.has_error) {
-               asn1_free(&data);
-               return False;
+       if (data->has_error) {
+               asn1_free(data);
+               return false;
        }
 
-       *result = data_blob_talloc(mem_ctx, 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,
@@ -521,9 +548,9 @@ static struct ldb_val **ldap_decode_substring(TALLOC_CTX *mem_ctx, struct ldb_va
        if (chunks[chunk_num]->data == NULL) {
                return NULL;
        }
-       chunks[chunk_num]->length = strlen(value) + 1;
+       chunks[chunk_num]->length = strlen(value);
 
-       chunks[chunk_num + 1] = NULL;
+       chunks[chunk_num + 1] = '\0';
 
        return chunks;
 }
@@ -604,7 +631,7 @@ static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx,
 
                asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
                asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
-               asn1_read_OctetString(data, &value);
+               asn1_read_OctetString(data, mem_ctx, &value);
                asn1_end_tag(data);
                if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
                        goto failed;
@@ -626,7 +653,7 @@ static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx,
                if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
                        goto failed;
                }
-               if (!asn1_read_OctetString(data, &attr)) {
+               if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
                        goto failed;
                }
 
@@ -646,7 +673,7 @@ static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx,
                        if (subs_tag > 2) goto failed;
 
                        asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(subs_tag));
-                       asn1_read_LDAPString(data, &value);
+                       asn1_read_LDAPString(data, mem_ctx, &value);
                        asn1_end_tag(data);
 
                        switch (subs_tag) {
@@ -716,7 +743,7 @@ static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx,
 
                asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
                asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
-               asn1_read_OctetString(data, &value);
+               asn1_read_OctetString(data, mem_ctx, &value);
                asn1_end_tag(data);
                if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
                        goto failed;
@@ -735,7 +762,7 @@ static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx,
 
                asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
                asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
-               asn1_read_OctetString(data, &value);
+               asn1_read_OctetString(data, mem_ctx, &value);
                asn1_end_tag(data);
                if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
                        goto failed;
@@ -754,7 +781,7 @@ static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx,
                if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(filter_tag))) {
                        goto failed;
                }
-               if (!asn1_read_LDAPString(data, &attr)) {
+               if (!asn1_read_LDAPString(data, ret, &attr)) {
                        goto failed;
                }
 
@@ -773,7 +800,7 @@ static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx,
 
                asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
                asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
-               asn1_read_OctetString(data, &value);
+               asn1_read_OctetString(data, mem_ctx, &value);
                asn1_end_tag(data);
                if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
                        goto failed;
@@ -798,16 +825,16 @@ static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx,
                /* 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, &oid);
+                       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, &attr);
+                       asn1_read_LDAPString(data, ret, &attr);
                        asn1_end_tag(data);
                }
                asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(3));
-               asn1_read_LDAPString(data, &value);
+               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
@@ -875,7 +902,7 @@ static void ldap_decode_attrib(TALLOC_CTX *mem_ctx, struct asn1_data *data,
        asn1_start_tag(data, ASN1_SET);
        while (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
                DATA_BLOB blob;
-               asn1_read_OctetString(data, &blob);
+               asn1_read_OctetString(data, mem_ctx, &blob);
                add_value_to_attrib(mem_ctx, &blob, attrib);
        }
        asn1_end_tag(data);
@@ -898,7 +925,9 @@ 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_t tag;
 
@@ -906,7 +935,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
        asn1_read_Integer(data, &msg->messageid);
 
        if (!asn1_peek_uint8(data, &tag))
-               return False;
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
 
        switch(tag) {
 
@@ -922,8 +951,14 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                        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, 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;
@@ -934,14 +969,22 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                        r->mechanism = LDAP_AUTH_MECH_SASL;
                        asn1_read_OctetString_talloc(msg, data, &r->creds.SASL.mechanism);
                        if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { /* optional */
-                               asn1_read_OctetString(data, &r->creds.SASL.secblob);
-                               if (r->creds.SASL.secblob.data) {
-                                       talloc_steal(msg, r->creds.SASL.secblob.data);
+                               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 = data_blob(NULL, 0);
+                               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;
@@ -955,10 +998,15 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                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, 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;
@@ -984,7 +1032,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
 
                r->tree = ldap_decode_filter_tree(msg, data);
                if (r->tree == NULL) {
-                       return False;
+                       return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
                }
 
                asn1_start_tag(data, ASN1_SEQUENCE(0));
@@ -992,15 +1040,16 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                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, data,
                                                          &attr))
-                               return False;
+                               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);
@@ -1059,8 +1108,9 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                        ldap_decode_attrib(msg, data, &mod.attrib);
                        asn1_end_tag(data);
                        if (!add_mod_to_array_talloc(msg, &mod,
-                                                    &r->mods, &r->num_mods))
-                               break;
+                                                    &r->mods, &r->num_mods)) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
                }
 
                asn1_end_tag(data);
@@ -1109,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, 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);
@@ -1142,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, 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;
@@ -1171,7 +1228,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                asn1_read_OctetString_talloc(msg, data, &r->dn);
                asn1_start_tag(data, ASN1_SEQUENCE(0));
                asn1_read_OctetString_talloc(msg, data, &r->attribute);
-               asn1_read_OctetString(data, &r->value);
+               asn1_read_OctetString(data, msg, &r->value);
                if (r->value.data) {
                        talloc_steal(msg, r->value.data);
                }
@@ -1205,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, 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, 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);
@@ -1227,65 +1288,105 @@ 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, 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;
+
+               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(msg, 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, 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));
+       if ((data->has_error) || (data->nesting != NULL)) {
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+       }
+       return NT_STATUS_OK;
 }
 
 
@@ -1293,7 +1394,7 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
   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_BLOB blob, size_t *packet_size)
+NTSTATUS ldap_full_packet(void *private_data, DATA_BLOB blob, size_t *packet_size)
 {
        return asn1_full_tag(blob, ASN1_SEQUENCE(0), packet_size);
 }