Remove restrictions on number of DN components in LDAP server
[kai/samba.git] / source4 / ldap_server / ldap_backend.c
index 7f733390c14aead6386847ed8d16f775f160353e..2adff2a1dfad5d5e9c43075dddc3fb4a2f6196ac 100644 (file)
@@ -5,7 +5,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 "ldap_server/ldap_server.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/ldap/ldap.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+#include "smbd/service_stream.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/ldb_wrap.h"
+
+#define VALID_DN_SYNTAX(dn) do {\
+       if (!(dn)) {\
+               return NT_STATUS_NO_MEMORY;\
+       } else if ( ! ldb_dn_validate(dn)) {\
+               result = LDAP_INVALID_DN_SYNTAX;\
+               errstr = "Invalid DN format";\
+               goto reply;\
+       }\
+} while(0)
+
+static int map_ldb_error(struct ldb_context *ldb, int err, const char **errstring)
+{
+       *errstring = ldb_errstring(ldb);
+       
+       /* its 1:1 for now */
+       return err;
+}
 
+/*
+  connect to the sam database
+*/
+NTSTATUS ldapsrv_backend_Init(struct ldapsrv_connection *conn) 
+{
+       conn->ldb = ldb_wrap_connect(conn, 
+                                    conn->connection->event.ctx,
+                                    conn->lp_ctx,
+                                    lp_sam_url(conn->lp_ctx), 
+                                    conn->session_info,
+                                    samdb_credentials(conn, conn->connection->event.ctx, conn->lp_ctx), 
+                                    conn->global_catalog ? LDB_FLG_RDONLY : 0, NULL);
+       if (conn->ldb == NULL) {
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       }
 
-struct ldapsrv_reply *ldapsrv_init_reply(struct ldapsrv_call *call, enum ldap_request_tag type)
+       if (conn->server_credentials) {
+               char **sasl_mechs = NULL;
+               struct gensec_security_ops **backends = gensec_security_all();
+               struct gensec_security_ops **ops
+                       = gensec_use_kerberos_mechs(conn, backends, conn->server_credentials);
+               int i, j = 0;
+               for (i = 0; ops && ops[i]; i++) {
+                       if (!gensec_security_ops_enabled(ops[i], conn->lp_ctx))
+                               continue;
+
+                       if (ops[i]->sasl_name && ops[i]->server_start) {
+                               char *sasl_name = talloc_strdup(conn, ops[i]->sasl_name);
+
+                               if (!sasl_name) {
+                                       return NT_STATUS_NO_MEMORY;
+                               }
+                               sasl_mechs = talloc_realloc(conn, sasl_mechs, char *, j + 2);
+                               if (!sasl_mechs) {
+                                       return NT_STATUS_NO_MEMORY;
+                               }
+                               sasl_mechs[j] = sasl_name;
+                               talloc_steal(sasl_mechs, sasl_name);
+                               sasl_mechs[j+1] = NULL;
+                               j++;
+                       }
+               }
+               talloc_free(ops);
+               ldb_set_opaque(conn->ldb, "supportedSASLMechanims", sasl_mechs);
+       }
+
+       return NT_STATUS_OK;
+}
+
+struct ldapsrv_reply *ldapsrv_init_reply(struct ldapsrv_call *call, uint8_t type)
 {
        struct ldapsrv_reply *reply;
 
-       reply = talloc_p(call, struct ldapsrv_reply);
+       reply = talloc(call, struct ldapsrv_reply);
        if (!reply) {
                return NULL;
        }
+       reply->msg = talloc(reply, struct ldap_message);
+       if (reply->msg == NULL) {
+               talloc_free(reply);
+               return NULL;
+       }
 
-       reply->prev = reply->next = NULL;
-       reply->state = LDAPSRV_REPLY_STATE_NEW;
-       reply->msg.messageid = call->request.messageid;
-       reply->msg.type = type;
-       reply->msg.mem_ctx = reply;
+       reply->msg->messageid = call->request->messageid;
+       reply->msg->type = type;
+       reply->msg->controls = NULL;
 
        return reply;
 }
 
-NTSTATUS ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *reply)
+void ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *reply)
 {
        DLIST_ADD_END(call->replies, reply, struct ldapsrv_reply *);
-       return NT_STATUS_OK;
 }
 
-struct ldapsrv_partition *ldapsrv_get_partition(struct ldapsrv_connection *conn, const char *dn)
-{
-       if (strcasecmp("", dn) == 0) {
-               return conn->service->rootDSE;
-       }
-
-       return conn->service->default_partition;
-}
-
-NTSTATUS ldapsrv_unwilling(struct ldapsrv_call *call, int error)
+static NTSTATUS ldapsrv_unwilling(struct ldapsrv_call *call, int error)
 {
        struct ldapsrv_reply *reply;
        struct ldap_ExtendedResponse *r;
 
-       DEBUG(10,("Unwilling type[%d] id[%d]\n", call->request.type, call->request.messageid));
+       DEBUG(10,("Unwilling type[%d] id[%d]\n", call->request->type, call->request->messageid));
 
        reply = ldapsrv_init_reply(call, LDAP_TAG_ExtendedResponse);
        if (!reply) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       r = &reply->msg.r.ExtendedResponse;
+       r = &reply->msg->r.ExtendedResponse;
        r->response.resultcode = error;
        r->response.dn = NULL;
        r->response.errormessage = NULL;
        r->response.referral = NULL;
-       r->name = NULL;
-       r->value.data = NULL;
-       r->value.length = 0;
+       r->oid = NULL;
+       r->value = NULL;
 
-       return ldapsrv_queue_reply(call, reply);
+       ldapsrv_queue_reply(call, reply);
+       return NT_STATUS_OK;
 }
 
-static NTSTATUS ldapsrv_BindRequest(struct ldapsrv_call *call)
+static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call)
 {
-       struct ldap_BindRequest *req = &call->request.r.BindRequest;
-       struct ldapsrv_reply *reply;
-       struct ldap_BindResponse *resp;
+       struct ldap_SearchRequest *req = &call->request->r.SearchRequest;
+       struct ldap_SearchResEntry *ent;
+       struct ldap_Result *done;
+       struct ldapsrv_reply *ent_r, *done_r;
+       void *local_ctx;
+       struct ldb_context *samdb = talloc_get_type(call->conn->ldb, struct ldb_context);
+       struct ldb_dn *basedn;
+       struct ldb_result *res = NULL;
+       struct ldb_request *lreq;
+       struct ldb_control *search_control;
+       struct ldb_search_options_control *search_options;
+       enum ldb_scope scope = LDB_SCOPE_DEFAULT;
+       const char **attrs = NULL;
+       const char *scope_str, *errstr = NULL;
+       int success_limit = 1;
+       int result = -1;
+       int ldb_ret = -1;
+       int i, j;
 
-       DEBUG(10, ("BindRequest dn: %s\n",req->dn));
+       DEBUG(10, ("SearchRequest"));
+       DEBUGADD(10, (" basedn: %s", req->basedn));
+       DEBUGADD(10, (" filter: %s\n", ldb_filter_from_tree(call, req->tree)));
+
+       local_ctx = talloc_new(call);
+       NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+       basedn = ldb_dn_new(local_ctx, samdb, req->basedn);
+       VALID_DN_SYNTAX(basedn);
+
+       DEBUG(10, ("SearchRequest: basedn: [%s]\n", req->basedn));
+       DEBUG(10, ("SearchRequest: filter: [%s]\n", ldb_filter_from_tree(call, req->tree)));
+
+       switch (req->scope) {
+               case LDAP_SEARCH_SCOPE_BASE:
+                       scope_str = "BASE";
+                       scope = LDB_SCOPE_BASE;
+                       success_limit = 0;
+                       break;
+               case LDAP_SEARCH_SCOPE_SINGLE:
+                       scope_str = "ONE";
+                       scope = LDB_SCOPE_ONELEVEL;
+                       success_limit = 0;
+                       break;
+               case LDAP_SEARCH_SCOPE_SUB:
+                       scope_str = "SUB";
+                       scope = LDB_SCOPE_SUBTREE;
+                       success_limit = 0;
+                       break;
+               default:
+                       result = LDAP_PROTOCOL_ERROR;
+                       errstr = "Invalid scope";
+                       goto reply;
+       }
+       DEBUG(10,("SearchRequest: scope: [%s]\n", scope_str));
 
-       reply = ldapsrv_init_reply(call, LDAP_TAG_BindResponse);
-       if (!reply) {
-               return NT_STATUS_NO_MEMORY;
+       if (req->num_attributes >= 1) {
+               attrs = talloc_array(local_ctx, const char *, req->num_attributes+1);
+               NT_STATUS_HAVE_NO_MEMORY(attrs);
+
+               for (i=0; i < req->num_attributes; i++) {
+                       DEBUG(10,("SearchRequest: attrs: [%s]\n",req->attributes[i]));
+                       attrs[i] = req->attributes[i];
+               }
+               attrs[i] = NULL;
        }
 
-       resp = &reply->msg.r.BindResponse;
-       resp->response.resultcode = 0;
-       resp->response.dn = NULL;
-       resp->response.errormessage = NULL;
-       resp->response.referral = NULL;
-       resp->SASL.secblob = data_blob(NULL, 0);
+       DEBUG(5,("ldb_request %s dn=%s filter=%s\n", 
+                scope_str, req->basedn, ldb_filter_from_tree(call, req->tree)));
 
-       return ldapsrv_queue_reply(call, reply);
-}
+       res = talloc_zero(local_ctx, struct ldb_result);
+       NT_STATUS_HAVE_NO_MEMORY(res);
 
-static NTSTATUS ldapsrv_UnbindRequest(struct ldapsrv_call *call)
-{
-/*     struct ldap_UnbindRequest *req = &call->request->r.UnbindRequest;*/
-       DEBUG(10, ("UnbindRequest\n"));
-       return NT_STATUS_OK;
-}
+       ldb_ret = ldb_build_search_req_ex(&lreq, samdb, local_ctx,
+                                         basedn, scope,
+                                         req->tree, attrs,
+                                         call->request->controls,
+                                         res, ldb_search_default_callback,
+                                         NULL);
 
-static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call)
-{
-       struct ldap_SearchRequest *req = &call->request.r.SearchRequest;
-       struct ldapsrv_partition *part;
+       if (ldb_ret != LDB_SUCCESS) {
+               goto reply;
+       }
 
-       DEBUG(10, ("SearchRequest"));
-       DEBUGADD(10, (" basedn: %s", req->basedn));
-       DEBUGADD(10, (" filter: %s\n", req->filter));
+       if (call->conn->global_catalog) {
+               search_control = ldb_request_get_control(lreq, LDB_CONTROL_SEARCH_OPTIONS_OID);
+
+               search_options = NULL;
+               if (search_control) {
+                       search_options = talloc_get_type(search_control->data, struct ldb_search_options_control);
+                       search_options->search_options |= LDB_SEARCH_OPTION_PHANTOM_ROOT;
+               } else {
+                       search_options = talloc(lreq, struct ldb_search_options_control);
+                       NT_STATUS_HAVE_NO_MEMORY(search_options);
+                       search_options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
+                       ldb_request_add_control(lreq, LDB_CONTROL_SEARCH_OPTIONS_OID, false, search_options);
+               }
+       }
+       ldb_set_timeout(samdb, lreq, req->timelimit);
 
-       part = ldapsrv_get_partition(call->conn, req->basedn);
+       ldb_ret = ldb_request(samdb, lreq);
 
-       if (!part->ops->Search) {
-               struct ldap_Result *done;
-               struct ldapsrv_reply *done_r;
+       if (ldb_ret != LDB_SUCCESS) {
+               goto reply;
+       }
 
-               done_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultDone);
-               if (!done_r) {
-                       return NT_STATUS_NO_MEMORY;
+       ldb_ret = ldb_wait(lreq->handle, LDB_WAIT_ALL);
+
+       if (ldb_ret == LDB_SUCCESS) {
+               for (i = 0; i < res->count; i++) {
+                       ent_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultEntry);
+                       NT_STATUS_HAVE_NO_MEMORY(ent_r);
+
+                       /* Better to have the whole message kept here,
+                        * than to find someone further up didn't put
+                        * a value in the right spot in the talloc tree */
+                       talloc_steal(ent_r, res->msgs[i]);
+                       
+                       ent = &ent_r->msg->r.SearchResultEntry;
+                       ent->dn = ldb_dn_alloc_linearized(ent_r, res->msgs[i]->dn);
+                       ent->num_attributes = 0;
+                       ent->attributes = NULL;
+                       if (res->msgs[i]->num_elements == 0) {
+                               goto queue_reply;
+                       }
+                       ent->num_attributes = res->msgs[i]->num_elements;
+                       ent->attributes = talloc_array(ent_r, struct ldb_message_element, ent->num_attributes);
+                       NT_STATUS_HAVE_NO_MEMORY(ent->attributes);
+                       for (j=0; j < ent->num_attributes; j++) {
+                               ent->attributes[j].name = talloc_steal(ent->attributes, res->msgs[i]->elements[j].name);
+                               ent->attributes[j].num_values = 0;
+                               ent->attributes[j].values = NULL;
+                               if (req->attributesonly && (res->msgs[i]->elements[j].num_values == 0)) {
+                                       continue;
+                               }
+                               ent->attributes[j].num_values = res->msgs[i]->elements[j].num_values;
+                               ent->attributes[j].values = res->msgs[i]->elements[j].values;
+                               talloc_steal(ent->attributes, res->msgs[i]->elements[j].values);
+                       }
+queue_reply:
+                       ldapsrv_queue_reply(call, ent_r);
                }
+       }
 
-               done = &done_r->msg.r.SearchResultDone;
-               done->resultcode = 53;
-               done->dn = NULL;
-               done->errormessage = NULL;
-               done->referral = NULL;
+reply:
+       done_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultDone);
+       NT_STATUS_HAVE_NO_MEMORY(done_r);
 
-               return ldapsrv_queue_reply(call, done_r);
+       done = &done_r->msg->r.SearchResultDone;
+       done->dn = NULL;
+       done->referral = NULL;
+
+       if (result != -1) {
+       } else if (ldb_ret == LDB_SUCCESS) {
+               if (res->count >= success_limit) {
+                       DEBUG(10,("SearchRequest: results: [%d]\n", res->count));
+                       result = LDAP_SUCCESS;
+                       errstr = NULL;
+               }
+               if (res->controls) {
+                       done_r->msg->controls = res->controls;
+                       talloc_steal(done_r, res->controls);
+               }
+       } else {
+               DEBUG(10,("SearchRequest: error\n"));
+               result = map_ldb_error(samdb, ldb_ret, &errstr);
        }
 
-       return part->ops->Search(part, call, req);
+       done->resultcode = result;
+       done->errormessage = (errstr?talloc_strdup(done_r, errstr):NULL);
+
+       talloc_free(local_ctx);
+
+       ldapsrv_queue_reply(call, done_r);
+       return NT_STATUS_OK;
 }
 
 static NTSTATUS ldapsrv_ModifyRequest(struct ldapsrv_call *call)
 {
-       struct ldap_ModifyRequest *req = &call->request.r.ModifyRequest;
-       struct ldapsrv_partition *part;
+       struct ldap_ModifyRequest *req = &call->request->r.ModifyRequest;
+       struct ldap_Result *modify_result;
+       struct ldapsrv_reply *modify_reply;
+       void *local_ctx;
+       struct ldb_context *samdb = call->conn->ldb;
+       struct ldb_message *msg = NULL;
+       struct ldb_dn *dn;
+       const char *errstr = NULL;
+       int result = LDAP_SUCCESS;
+       int ldb_ret;
+       int i,j;
 
        DEBUG(10, ("ModifyRequest"));
        DEBUGADD(10, (" dn: %s", req->dn));
 
-       part = ldapsrv_get_partition(call->conn, req->dn);
+       local_ctx = talloc_named(call, 0, "ModifyRequest local memory context");
+       NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+       dn = ldb_dn_new(local_ctx, samdb, req->dn);
+       VALID_DN_SYNTAX(dn);
+
+       DEBUG(10, ("ModifyRequest: dn: [%s]\n", req->dn));
+
+       msg = talloc(local_ctx, struct ldb_message);
+       NT_STATUS_HAVE_NO_MEMORY(msg);
+
+       msg->dn = dn;
+       msg->num_elements = 0;
+       msg->elements = NULL;
+
+       if (req->num_mods > 0) {
+               msg->num_elements = req->num_mods;
+               msg->elements = talloc_array(msg, struct ldb_message_element, req->num_mods);
+               NT_STATUS_HAVE_NO_MEMORY(msg->elements);
+
+               for (i=0; i < msg->num_elements; i++) {
+                       msg->elements[i].name = discard_const_p(char, req->mods[i].attrib.name);
+                       msg->elements[i].num_values = 0;
+                       msg->elements[i].values = NULL;
+
+                       switch (req->mods[i].type) {
+                       default:
+                               result = LDAP_PROTOCOL_ERROR;
+                               errstr = "Invalid LDAP_MODIFY_* type";
+                               goto reply;
+                       case LDAP_MODIFY_ADD:
+                               msg->elements[i].flags = LDB_FLAG_MOD_ADD;
+                               break;
+                       case LDAP_MODIFY_DELETE:
+                               msg->elements[i].flags = LDB_FLAG_MOD_DELETE;
+                               break;
+                       case LDAP_MODIFY_REPLACE:
+                               msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+                               break;
+                       }
+
+                       msg->elements[i].num_values = req->mods[i].attrib.num_values;
+                       if (msg->elements[i].num_values > 0) {
+                               msg->elements[i].values = talloc_array(msg->elements, struct ldb_val,
+                                                                      msg->elements[i].num_values);
+                               NT_STATUS_HAVE_NO_MEMORY(msg->elements[i].values);
+
+                               for (j=0; j < msg->elements[i].num_values; j++) {
+                                       if (!(req->mods[i].attrib.values[j].length > 0)) {
+                                               result = LDAP_OTHER;
+                                               errstr = "Empty attribute values are not allowed";
+                                               goto reply;
+                                       }
+                                       msg->elements[i].values[j].length = req->mods[i].attrib.values[j].length;
+                                       msg->elements[i].values[j].data = req->mods[i].attrib.values[j].data;                   
+                               }
+                       }
+               }
+       } else {
+               result = LDAP_OTHER;
+               errstr = "No mods are not allowed";
+               goto reply;
+       }
+
+reply:
+       modify_reply = ldapsrv_init_reply(call, LDAP_TAG_ModifyResponse);
+       NT_STATUS_HAVE_NO_MEMORY(modify_reply);
 
-       if (!part->ops->Modify) {
-               return ldapsrv_unwilling(call, 53);
+       if (result == LDAP_SUCCESS) {
+               ldb_ret = ldb_modify(samdb, msg);
+               result = map_ldb_error(samdb, ldb_ret, &errstr);
        }
 
-       return part->ops->Modify(part, call, req);
+       modify_result = &modify_reply->msg->r.AddResponse;
+       modify_result->dn = NULL;
+       modify_result->resultcode = result;
+       modify_result->errormessage = (errstr?talloc_strdup(modify_reply, errstr):NULL);
+       modify_result->referral = NULL;
+
+       talloc_free(local_ctx);
+
+       ldapsrv_queue_reply(call, modify_reply);
+       return NT_STATUS_OK;
+
 }
 
 static NTSTATUS ldapsrv_AddRequest(struct ldapsrv_call *call)
 {
-       struct ldap_AddRequest *req = &call->request.r.AddRequest;
-       struct ldapsrv_partition *part;
+       struct ldap_AddRequest *req = &call->request->r.AddRequest;
+       struct ldap_Result *add_result;
+       struct ldapsrv_reply *add_reply;
+       void *local_ctx;
+       struct ldb_context *samdb = call->conn->ldb;
+       struct ldb_message *msg = NULL;
+       struct ldb_dn *dn;
+       const char *errstr = NULL;
+       int result = LDAP_SUCCESS;
+       int ldb_ret;
+       int i,j;
 
        DEBUG(10, ("AddRequest"));
        DEBUGADD(10, (" dn: %s", req->dn));
 
-       part = ldapsrv_get_partition(call->conn, req->dn);
+       local_ctx = talloc_named(call, 0, "AddRequest local memory context");
+       NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+       dn = ldb_dn_new(local_ctx, samdb, req->dn);
+       VALID_DN_SYNTAX(dn);
+
+       DEBUG(10, ("AddRequest: dn: [%s]\n", req->dn));
+
+       msg = talloc(local_ctx, struct ldb_message);
+       NT_STATUS_HAVE_NO_MEMORY(msg);
+
+       msg->dn = dn;
+       msg->num_elements = 0;
+       msg->elements = NULL;
+
+       if (req->num_attributes > 0) {
+               msg->num_elements = req->num_attributes;
+               msg->elements = talloc_array(msg, struct ldb_message_element, msg->num_elements);
+               NT_STATUS_HAVE_NO_MEMORY(msg->elements);
+
+               for (i=0; i < msg->num_elements; i++) {
+                       msg->elements[i].name = discard_const_p(char, req->attributes[i].name);
+                       msg->elements[i].flags = 0;
+                       msg->elements[i].num_values = 0;
+                       msg->elements[i].values = NULL;
+                       
+                       if (req->attributes[i].num_values > 0) {
+                               msg->elements[i].num_values = req->attributes[i].num_values;
+                               msg->elements[i].values = talloc_array(msg->elements, struct ldb_val,
+                                                                      msg->elements[i].num_values);
+                               NT_STATUS_HAVE_NO_MEMORY(msg->elements[i].values);
+
+                               for (j=0; j < msg->elements[i].num_values; j++) {
+                                       if (!(req->attributes[i].values[j].length > 0)) {
+                                               result = LDAP_OTHER;
+                                               errstr = "Empty attribute values are not allowed";
+                                               goto reply;
+                                       }
+                                       msg->elements[i].values[j].length = req->attributes[i].values[j].length;
+                                       msg->elements[i].values[j].data = req->attributes[i].values[j].data;                    
+                               }
+                       } else {
+                               result = LDAP_OTHER;
+                               errstr = "No attribute values are not allowed";
+                               goto reply;
+                       }
+               }
+       } else {
+               result = LDAP_OTHER;
+               errstr = "No attributes are not allowed";
+               goto reply;
+       }
+
+reply:
+       add_reply = ldapsrv_init_reply(call, LDAP_TAG_AddResponse);
+       NT_STATUS_HAVE_NO_MEMORY(add_reply);
 
-       if (!part->ops->Add) {
-               return ldapsrv_unwilling(call, 53);
+       if (result == LDAP_SUCCESS) {
+               ldb_ret = ldb_add(samdb, msg);
+               result = map_ldb_error(samdb, ldb_ret, &errstr);
        }
 
-       return part->ops->Add(part, call, req);
+       add_result = &add_reply->msg->r.AddResponse;
+       add_result->dn = NULL;
+       add_result->resultcode = result;
+       add_result->errormessage = (errstr?talloc_strdup(add_reply,errstr):NULL);
+       add_result->referral = NULL;
+
+       talloc_free(local_ctx);
+
+       ldapsrv_queue_reply(call, add_reply);
+       return NT_STATUS_OK;
+
 }
 
 static NTSTATUS ldapsrv_DelRequest(struct ldapsrv_call *call)
 {
-       struct ldap_DelRequest *req = &call->request.r.DelRequest;
-       struct ldapsrv_partition *part;
+       struct ldap_DelRequest *req = &call->request->r.DelRequest;
+       struct ldap_Result *del_result;
+       struct ldapsrv_reply *del_reply;
+       void *local_ctx;
+       struct ldb_context *samdb = call->conn->ldb;
+       struct ldb_dn *dn;
+       const char *errstr = NULL;
+       int result = LDAP_SUCCESS;
+       int ldb_ret;
 
        DEBUG(10, ("DelRequest"));
        DEBUGADD(10, (" dn: %s", req->dn));
 
-       part = ldapsrv_get_partition(call->conn, req->dn);
+       local_ctx = talloc_named(call, 0, "DelRequest local memory context");
+       NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+       dn = ldb_dn_new(local_ctx, samdb, req->dn);
+       VALID_DN_SYNTAX(dn);
+
+       DEBUG(10, ("DelRequest: dn: [%s]\n", req->dn));
 
-       if (!part->ops->Del) {
-               return ldapsrv_unwilling(call, 53);
+reply:
+       del_reply = ldapsrv_init_reply(call, LDAP_TAG_DelResponse);
+       NT_STATUS_HAVE_NO_MEMORY(del_reply);
+
+       if (result == LDAP_SUCCESS) {
+               ldb_ret = ldb_delete(samdb, dn);
+               result = map_ldb_error(samdb, ldb_ret, &errstr);
        }
 
-       return part->ops->Del(part, call, req);
+       del_result = &del_reply->msg->r.DelResponse;
+       del_result->dn = NULL;
+       del_result->resultcode = result;
+       del_result->errormessage = (errstr?talloc_strdup(del_reply,errstr):NULL);
+       del_result->referral = NULL;
+
+       talloc_free(local_ctx);
+
+       ldapsrv_queue_reply(call, del_reply);
+       return NT_STATUS_OK;
 }
 
 static NTSTATUS ldapsrv_ModifyDNRequest(struct ldapsrv_call *call)
 {
-       struct ldap_ModifyDNRequest *req = &call->request.r.ModifyDNRequest;
-       struct ldapsrv_partition *part;
+       struct ldap_ModifyDNRequest *req = &call->request->r.ModifyDNRequest;
+       struct ldap_Result *modifydn;
+       struct ldapsrv_reply *modifydn_r;
+       void *local_ctx;
+       struct ldb_context *samdb = call->conn->ldb;
+       struct ldb_dn *olddn, *newdn=NULL, *newrdn;
+       struct ldb_dn *parentdn = NULL;
+       const char *errstr = NULL;
+       int result = LDAP_SUCCESS;
+       int ldb_ret;
 
        DEBUG(10, ("ModifyDNRequrest"));
        DEBUGADD(10, (" dn: %s", req->dn));
        DEBUGADD(10, (" newrdn: %s", req->newrdn));
 
-       part = ldapsrv_get_partition(call->conn, req->dn);
+       local_ctx = talloc_named(call, 0, "ModifyDNRequest local memory context");
+       NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+       olddn = ldb_dn_new(local_ctx, samdb, req->dn);
+       VALID_DN_SYNTAX(olddn);
+
+       newrdn = ldb_dn_new(local_ctx, samdb, req->newrdn);
+       VALID_DN_SYNTAX(newrdn);
+
+       DEBUG(10, ("ModifyDNRequest: olddn: [%s]\n", req->dn));
+       DEBUG(10, ("ModifyDNRequest: newrdn: [%s]\n", req->newrdn));
 
-       if (!part->ops->ModifyDN) {
-               return ldapsrv_unwilling(call, 53);
+       /* we can't handle the rename if we should not remove the old dn */
+       if (!req->deleteolddn) {
+               result = LDAP_UNWILLING_TO_PERFORM;
+               errstr = "Old RDN must be deleted";
+               goto reply;
        }
 
-       return part->ops->ModifyDN(part, call, req);
+       if (req->newsuperior) {
+               parentdn = ldb_dn_new(local_ctx, samdb, req->newsuperior);
+               VALID_DN_SYNTAX(parentdn);
+               DEBUG(10, ("ModifyDNRequest: newsuperior: [%s]\n", req->newsuperior));
+               
+               if (ldb_dn_get_comp_num(parentdn) < 1) {
+                       result = LDAP_AFFECTS_MULTIPLE_DSAS;
+                       errstr = "Error new Superior DN invalid";
+                       goto reply;
+               }
+       }
+
+       if (!parentdn) {
+               parentdn = ldb_dn_get_parent(local_ctx, olddn);
+               NT_STATUS_HAVE_NO_MEMORY(parentdn);
+       }
+
+       if ( ! ldb_dn_add_child_fmt(parentdn,
+                               "%s=%s",
+                               ldb_dn_get_rdn_name(newrdn),
+                               (char *)ldb_dn_get_rdn_val(newrdn)->data)) {
+               result = LDAP_OTHER;
+               goto reply;
+       }
+       newdn = parentdn;
+
+reply:
+       modifydn_r = ldapsrv_init_reply(call, LDAP_TAG_ModifyDNResponse);
+       NT_STATUS_HAVE_NO_MEMORY(modifydn_r);
+
+       if (result == LDAP_SUCCESS) {
+               ldb_ret = ldb_rename(samdb, olddn, newdn);
+               result = map_ldb_error(samdb, ldb_ret, &errstr);
+       }
+
+       modifydn = &modifydn_r->msg->r.ModifyDNResponse;
+       modifydn->dn = NULL;
+       modifydn->resultcode = result;
+       modifydn->errormessage = (errstr?talloc_strdup(modifydn_r,errstr):NULL);
+       modifydn->referral = NULL;
+
+       talloc_free(local_ctx);
+
+       ldapsrv_queue_reply(call, modifydn_r);
+       return NT_STATUS_OK;
 }
 
 static NTSTATUS ldapsrv_CompareRequest(struct ldapsrv_call *call)
 {
-       struct ldap_CompareRequest *req = &call->request.r.CompareRequest;
-       struct ldapsrv_partition *part;
+       struct ldap_CompareRequest *req = &call->request->r.CompareRequest;
+       struct ldap_Result *compare;
+       struct ldapsrv_reply *compare_r;
+       void *local_ctx;
+       struct ldb_context *samdb = call->conn->ldb;
+       struct ldb_result *res = NULL;
+       struct ldb_dn *dn;
+       const char *attrs[1];
+       const char *errstr = NULL;
+       const char *filter = NULL;
+       int result = LDAP_SUCCESS;
+       int ldb_ret;
 
        DEBUG(10, ("CompareRequest"));
        DEBUGADD(10, (" dn: %s", req->dn));
 
-       part = ldapsrv_get_partition(call->conn, req->dn);
-
-       if (!part->ops->Compare) {
-               return ldapsrv_unwilling(call, 53);
+       local_ctx = talloc_named(call, 0, "CompareRequest local_memory_context");
+       NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+       dn = ldb_dn_new(local_ctx, samdb, req->dn);
+       VALID_DN_SYNTAX(dn);
+
+       DEBUG(10, ("CompareRequest: dn: [%s]\n", req->dn));
+       filter = talloc_asprintf(local_ctx, "(%s=%*s)", req->attribute, 
+                                (int)req->value.length, req->value.data);
+       NT_STATUS_HAVE_NO_MEMORY(filter);
+
+       DEBUGADD(10, ("CompareRequest: attribute: [%s]\n", filter));
+
+       attrs[0] = NULL;
+
+reply:
+       compare_r = ldapsrv_init_reply(call, LDAP_TAG_CompareResponse);
+       NT_STATUS_HAVE_NO_MEMORY(compare_r);
+
+       if (result == LDAP_SUCCESS) {
+               ldb_ret = ldb_search(samdb, local_ctx, &res,
+                                    dn, LDB_SCOPE_BASE, attrs, "%s", filter);
+               if (ldb_ret != LDB_SUCCESS) {
+                       result = map_ldb_error(samdb, ldb_ret, &errstr);
+                       DEBUG(10,("CompareRequest: error: %s\n", errstr));
+               } else if (res->count == 0) {
+                       DEBUG(10,("CompareRequest: doesn't matched\n"));
+                       result = LDAP_COMPARE_FALSE;
+                       errstr = NULL;
+               } else if (res->count == 1) {
+                       DEBUG(10,("CompareRequest: matched\n"));
+                       result = LDAP_COMPARE_TRUE;
+                       errstr = NULL;
+               } else if (res->count > 1) {
+                       result = LDAP_OTHER;
+                       errstr = "too many objects match";
+                       DEBUG(10,("CompareRequest: %d results: %s\n", res->count, errstr));
+               }
        }
 
-       return part->ops->Compare(part, call, req);
+       compare = &compare_r->msg->r.CompareResponse;
+       compare->dn = NULL;
+       compare->resultcode = result;
+       compare->errormessage = (errstr?talloc_strdup(compare_r,errstr):NULL);
+       compare->referral = NULL;
+
+       talloc_free(local_ctx);
+
+       ldapsrv_queue_reply(call, compare_r);
+       return NT_STATUS_OK;
 }
 
 static NTSTATUS ldapsrv_AbandonRequest(struct ldapsrv_call *call)
@@ -233,26 +723,21 @@ static NTSTATUS ldapsrv_AbandonRequest(struct ldapsrv_call *call)
        return NT_STATUS_OK;
 }
 
-static NTSTATUS ldapsrv_ExtendedRequest(struct ldapsrv_call *call)
+NTSTATUS ldapsrv_do_call(struct ldapsrv_call *call)
 {
-/*     struct ldap_ExtendedRequest *req = &call->request.r.ExtendedRequest;*/
-       struct ldapsrv_reply *reply;
-
-       DEBUG(10, ("Extended\n"));
-
-       reply = ldapsrv_init_reply(call, LDAP_TAG_ExtendedResponse);
-       if (!reply) {
-               return NT_STATUS_NO_MEMORY;
+       int i;
+       struct ldap_message *msg = call->request;
+       /* Check for undecoded critical extensions */
+       for (i=0; msg->controls && msg->controls[i]; i++) {
+               if (!msg->controls_decoded[i] && 
+                   msg->controls[i]->critical) {
+                       DEBUG(3, ("ldapsrv_do_call: Critical extension %s is not known to this server\n",
+                                 msg->controls[i]->oid));
+                       return ldapsrv_unwilling(call, LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
+               }
        }
 
-       ZERO_STRUCT(reply->msg.r);
-
-       return ldapsrv_queue_reply(call, reply);
-}
-
-NTSTATUS ldapsrv_do_call(struct ldapsrv_call *call)
-{
-       switch(call->request.type) {
+       switch(call->request->type) {
        case LDAP_TAG_BindRequest:
                return ldapsrv_BindRequest(call);
        case LDAP_TAG_UnbindRequest: