r11958: - fixed memory leaks in the ldb_result handling in ldb operations
[abartlet/samba.git/.git] / source4 / lib / ldb / ldb_ildap / ldb_ildap.c
index 3d47863067179abf858a88373f8a91731c0cdc61..e195ec24aa08d8a2e709290df0b293b7cc9c6ff3 100644 (file)
 #include "includes.h"
 #include "ldb/include/ldb.h"
 #include "ldb/include/ldb_private.h"
+#include "ldb/include/ldb_errors.h"
 #include "libcli/ldap/ldap.h"
 #include "libcli/ldap/ldap_client.h"
 #include "lib/cmdline/popt_common.h"
+#include "auth/auth.h"
 
 struct ildb_private {
        struct ldap_connection *ldap;
-       NTSTATUS last_rc;
        struct ldb_message *rootDSE;
+       struct ldb_context *ldb;
 };
 
+
+/*
+  map an ildap NTSTATUS to a ldb error code
+*/
+static int ildb_map_error(struct ildb_private *ildb, NTSTATUS status)
+{
+       if (NT_STATUS_IS_OK(status)) {
+               return LDB_SUCCESS;
+       }
+       talloc_free(ildb->ldb->err_string);
+       ildb->ldb->err_string = talloc_strdup(ildb, ldap_errstr(ildb->ldap, status));
+       if (NT_STATUS_IS_LDAP(status)) {
+               return NT_STATUS_LDAP_CODE(status);
+       }
+       return LDB_ERR_OPERATIONS_ERROR;
+}
+
 /*
   rename a record
 */
@@ -52,19 +71,22 @@ static int ildb_rename(struct ldb_module *module, const struct ldb_dn *olddn, co
        int ret = 0;
        char *old_dn;
        char *newrdn, *parentdn;
+       NTSTATUS status;
 
        /* ignore ltdb specials */
        if (ldb_dn_is_special(olddn) || ldb_dn_is_special(newdn)) {
-               return 0;
+               return LDB_SUCCESS;
        }
 
        local_ctx = talloc_named(ildb, 0, "ildb_rename local context");
        if (local_ctx == NULL) {
-               return -1;
+               ret = LDB_ERR_OPERATIONS_ERROR;
+               goto failed;
        }
 
        old_dn = ldb_dn_linearize(local_ctx, olddn);
        if (old_dn == NULL) {
+               ret = LDB_ERR_INVALID_DN_SYNTAX;
                goto failed;
        }
 
@@ -72,25 +94,22 @@ static int ildb_rename(struct ldb_module *module, const struct ldb_dn *olddn, co
                                            newdn->components[0].name,
                                            ldb_dn_escape_value(ildb, newdn->components[0].value));
        if (newrdn == NULL) {
+               ret = LDB_ERR_OPERATIONS_ERROR;
                goto failed;
        }
 
        parentdn = ldb_dn_linearize(local_ctx, ldb_dn_get_parent(ildb, newdn));
        if (parentdn == NULL) {
+               ret = LDB_ERR_INVALID_DN_SYNTAX;
                goto failed;
        }
 
-       ildb->last_rc = ildap_rename(ildb->ldap, old_dn, newrdn, parentdn, True);
-       if (!NT_STATUS_IS_OK(ildb->last_rc)) {
-               ret = -1;
-       }
-
-       talloc_free(local_ctx);
-       return ret;
+       status = ildap_rename(ildb->ldap, old_dn, newrdn, parentdn, True);
+       ret = ildb_map_error(ildb, status);
 
 failed:
        talloc_free(local_ctx);
-       return -1;
+       return ret;
 }
 
 /*
@@ -101,19 +120,21 @@ static int ildb_delete(struct ldb_module *module, const struct ldb_dn *dn)
        struct ildb_private *ildb = module->private_data;
        char *del_dn;
        int ret = 0;
+       NTSTATUS status;
 
        /* ignore ltdb specials */
        if (ldb_dn_is_special(dn)) {
-               return 0;
+               return LDB_SUCCESS;
        }
        
        del_dn = ldb_dn_linearize(ildb, dn);
-
-       ildb->last_rc = ildap_delete(ildb->ldap, del_dn);
-       if (!NT_STATUS_IS_OK(ildb->last_rc)) {
-               ret = -1;
+       if (del_dn == NULL) {
+               return LDB_ERR_INVALID_DN_SYNTAX;
        }
 
+       status = ildap_delete(ildb->ldap, del_dn);
+       ret = ildb_map_error(ildb, status);
+
        talloc_free(del_dn);
 
        return ret;
@@ -123,16 +144,17 @@ static int ildb_delete(struct ldb_module *module, const struct ldb_dn *dn)
 static void ildb_rootdse(struct ldb_module *module);
 
 /*
-  search for matching records
+  search for matching records using a ldb_parse_tree
 */
-static int ildb_search(struct ldb_module *module, const struct ldb_dn *base,
-                      enum ldb_scope scope, const char *expression,
-                      const char * const *attrs, struct ldb_message ***res)
+static int ildb_search_bytree(struct ldb_module *module, const struct ldb_dn *base,
+                             enum ldb_scope scope, struct ldb_parse_tree *tree,
+                             const char * const *attrs, struct ldb_result **res)
 {
        struct ildb_private *ildb = module->private_data;
        int count, i;
        struct ldap_message **ldapres, *msg;
        char *search_base;
+       NTSTATUS status;
 
        if (scope == LDB_SCOPE_DEFAULT) {
                scope = LDB_SCOPE_SUBTREE;
@@ -153,33 +175,47 @@ static int ildb_search(struct ldb_module *module, const struct ldb_dn *base,
                search_base = ldb_dn_linearize(ildb, base);
        }
        if (search_base == NULL) {
-               return -1;
+               ldb_set_errstring(module, talloc_asprintf(module, "Unable to determine baseDN"));
+               return LDB_ERR_OTHER;
+       }
+       if (tree == NULL) {
+               ldb_set_errstring(module, talloc_asprintf(module, "Invalid expression parse tree"));
+               return LDB_ERR_OTHER;
        }
 
-       if (expression == NULL || expression[0] == '\0') {
-               expression = "objectClass=*";
+       (*res) = talloc(ildb, struct ldb_result);
+       if (! *res) {
+               return LDB_ERR_OTHER;
        }
+       (*res)->count = 0;
+       (*res)->msgs = NULL;
 
-       ildb->last_rc = ildap_search(ildb->ldap, search_base, scope, expression, attrs, 
-                                    0, &ldapres);
+       status = ildap_search_bytree(ildb->ldap, search_base, scope, tree, attrs, 
+                                           0, &ldapres);
        talloc_free(search_base);
-       if (!NT_STATUS_IS_OK(ildb->last_rc)) {
-               return -1;
+       if (!NT_STATUS_IS_OK(status)) {
+               ildb_map_error(ildb, status);
+               return LDB_ERR_OTHER;
        }
 
        count = ildap_count_entries(ildb->ldap, ldapres);
-       if (count == -1 || count == 0) {
+       if (count == -1) {
                talloc_free(ldapres);
-               return count;
+               return LDB_ERR_OTHER;
        }
 
-       (*res) = talloc_array(ildb, struct ldb_message *, count+1);
-       if (! *res) {
+       if (count == 0) {
                talloc_free(ldapres);
-               return -1;
+               return LDB_SUCCESS;
+       }
+
+       (*res)->msgs = talloc_array(*res, struct ldb_message *, count + 1);
+       if (! (*res)->msgs) {
+               talloc_free(ldapres);
+               return LDB_ERR_OTHER;
        }
 
-       (*res)[0] = NULL;
+       (*res)->msgs[0] = NULL;
 
        /* loop over all messages */
        for (i=0;i<count;i++) {
@@ -188,49 +224,29 @@ static int ildb_search(struct ldb_module *module, const struct ldb_dn *base,
                msg = ldapres[i];
                search = &msg->r.SearchResultEntry;
 
-               (*res)[i] = talloc(*res, struct ldb_message);
-               if (!(*res)[i]) {
+               (*res)->msgs[i] = talloc(*res, struct ldb_message);
+               if (!(*res)->msgs[i]) {
                        goto failed;
                }
-               (*res)[i+1] = NULL;
+               (*res)->msgs[i+1] = NULL;
 
-               (*res)[i]->dn = ldb_dn_explode((*res)[i], search->dn);
-               if ((*res)[i]->dn == NULL) {
+               (*res)->msgs[i]->dn = ldb_dn_explode((*res)->msgs[i], search->dn);
+               if ((*res)->msgs[i]->dn == NULL) {
                        goto failed;
                }
-               (*res)[i]->num_elements = search->num_attributes;
-               (*res)[i]->elements = talloc_steal((*res)[i], search->attributes);
-               (*res)[i]->private_data = NULL;
+               (*res)->msgs[i]->num_elements = search->num_attributes;
+               (*res)->msgs[i]->elements = talloc_steal((*res)->msgs[i], search->attributes);
+               (*res)->msgs[i]->private_data = NULL;
        }
 
        talloc_free(ldapres);
 
-       return count;
+       (*res)->count = count;
+       return LDB_SUCCESS;
 
 failed:
        if (*res) talloc_free(*res);
-       return -1;
-}
-
-
-/*
-  search for matching records using a ldb_parse_tree
-*/
-static int ildb_search_bytree(struct ldb_module *module, const struct ldb_dn *base,
-                             enum ldb_scope scope, struct ldb_parse_tree *tree,
-                             const char * const *attrs, struct ldb_message ***res)
-{
-       struct ildb_private *ildb = module->private_data;
-       char *expression;
-       int ret;
-
-       expression = ldb_filter_from_tree(ildb, tree);
-       if (expression == NULL) {
-               return -1;
-       }
-       ret = ildb_search(module, base, scope, expression, attrs, res);
-       talloc_free(expression);
-       return ret;
+       return LDB_ERR_OTHER;
 }
 
 
@@ -297,27 +313,26 @@ static int ildb_add(struct ldb_module *module, const struct ldb_message *msg)
        struct ldap_mod **mods;
        char *dn;
        int ret = 0;
+       NTSTATUS status;
 
        /* ignore ltdb specials */
        if (ldb_dn_is_special(msg->dn)) {
-               return 0;
+               return LDB_SUCCESS;
        }
 
        mods = ildb_msg_to_mods(ldb, msg, 0);
        if (mods == NULL) {
-               return -1;
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
        dn = ldb_dn_linearize(mods, msg->dn);
        if (dn == NULL) {
                talloc_free(mods);
-               return -1;
+               return LDB_ERR_INVALID_DN_SYNTAX;
        }
 
-       ildb->last_rc = ildap_add(ildb->ldap, dn, mods);
-       if (!NT_STATUS_IS_OK(ildb->last_rc)) {
-               ret = -1;
-       }
+       status = ildap_add(ildb->ldap, dn, mods);
+       ret = ildb_map_error(ildb, status);
 
        talloc_free(mods);
 
@@ -335,84 +350,91 @@ static int ildb_modify(struct ldb_module *module, const struct ldb_message *msg)
        struct ldap_mod **mods;
        char *dn;
        int ret = 0;
+       NTSTATUS status;
 
        /* ignore ltdb specials */
        if (ldb_dn_is_special(msg->dn)) {
-               return 0;
+               return LDB_SUCCESS;
        }
 
        mods = ildb_msg_to_mods(ldb, msg, 1);
        if (mods == NULL) {
-               return -1;
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
        dn = ldb_dn_linearize(mods, msg->dn);
        if (dn == NULL) {
                talloc_free(mods);
-               return -1;
+               return LDB_ERR_INVALID_DN_SYNTAX;
        }
 
-       ildb->last_rc = ildap_modify(ildb->ldap, dn, mods);
-       if (!NT_STATUS_IS_OK(ildb->last_rc)) {
-               ret = -1;
-       }
+       status = ildap_modify(ildb->ldap, dn, mods);
+       ret = ildb_map_error(ildb, status);
 
        talloc_free(mods);
 
        return ret;
 }
 
-static int ildb_lock(struct ldb_module *module, const char *lockname)
+static int ildb_start_trans(struct ldb_module *module)
 {
-       int ret = 0;
-
-       if (lockname == NULL) {
-               return -1;
-       }
-
        /* TODO implement a local locking mechanism here */
 
-       return ret;
+       return 0;
 }
 
-static int ildb_unlock(struct ldb_module *module, const char *lockname)
+static int ildb_end_trans(struct ldb_module *module)
 {
-       int ret = 0;
+       /* TODO implement a local transaction mechanism here */
 
-       if (lockname == NULL) {
-               return -1;
-       }
+       return 0;
+}
 
-       /* TODO implement a local unlocking mechanism here */
+static int ildb_del_trans(struct ldb_module *module)
+{
+       /* TODO implement a local locking mechanism here */
 
-       return ret;
+       return 0;
 }
 
-/*
-  return extended error information
-*/
-static const char *ildb_errstring(struct ldb_module *module)
+static int ildb_request(struct ldb_module *module, struct ldb_request *req)
 {
-       struct ildb_private *ildb = talloc_get_type(module->private_data, 
-                                                   struct ildb_private);
-       if (ildb == NULL) {
-               return "ildap not connected";
+       switch (req->operation) {
+
+       case LDB_REQ_SEARCH:
+               return ildb_search_bytree(module,
+                                         req->op.search.base,
+                                         req->op.search.scope, 
+                                         req->op.search.tree, 
+                                         req->op.search.attrs, 
+                                         &req->op.search.res);
+
+       case LDB_REQ_ADD:
+               return ildb_add(module, req->op.add.message);
+
+       case LDB_REQ_MODIFY:
+               return ildb_modify(module, req->op.mod.message);
+
+       case LDB_REQ_DELETE:
+               return ildb_delete(module, req->op.del.dn);
+
+       case LDB_REQ_RENAME:
+               return ildb_rename(module,
+                                       req->op.rename.olddn,
+                                       req->op.rename.newdn);
+
+       default:
+               return -1;
+
        }
-       return ldap_errstr(ildb->ldap, ildb->last_rc);
 }
 
-
 static const struct ldb_module_ops ildb_ops = {
-       .name          = "ldap",
-       .search        = ildb_search,
-       .search_bytree = ildb_search_bytree,
-       .add_record    = ildb_add,
-       .modify_record = ildb_modify,
-       .delete_record = ildb_delete,
-       .rename_record = ildb_rename,
-       .named_lock    = ildb_lock,
-       .named_unlock  = ildb_unlock,
-       .errstring     = ildb_errstring
+       .name              = "ldap",
+       .request           = ildb_request,
+       .start_transaction = ildb_start_trans,
+       .end_transaction   = ildb_end_trans,
+       .del_transaction   = ildb_del_trans
 };
 
 
@@ -422,14 +444,16 @@ static const struct ldb_module_ops ildb_ops = {
 static void ildb_rootdse(struct ldb_module *module)
 {
        struct ildb_private *ildb = module->private_data;
-       struct ldb_message **res = NULL;
+       struct ldb_result *res = NULL;
        struct ldb_dn *empty_dn = ldb_dn_new(ildb);
        int ret;
-       ret = ildb_search(module, empty_dn, LDB_SCOPE_BASE, "dn=dc=rootDSE", NULL, &res);
-       if (ret == 1) {
-               ildb->rootDSE = talloc_steal(ildb, res[0]);
+       ret = ildb_search_bytree(module, empty_dn, LDB_SCOPE_BASE, 
+                                ldb_parse_tree(empty_dn, "dn=dc=rootDSE"), 
+                                NULL, &res);
+       if (ret == LDB_SUCCESS && res->count == 1) {
+               ildb->rootDSE = talloc_steal(ildb, res->msgs[0]);
        }
-       if (ret != -1) talloc_free(res);
+       if (ret == LDB_SUCCESS) talloc_free(res);
        talloc_free(empty_dn);
 }
 
@@ -442,6 +466,7 @@ int ildb_connect(struct ldb_context *ldb, const char *url,
 {
        struct ildb_private *ildb = NULL;
        NTSTATUS status;
+       struct cli_credentials *creds;
 
        ildb = talloc(ldb, struct ildb_private);
        if (!ildb) {
@@ -450,6 +475,7 @@ int ildb_connect(struct ldb_context *ldb, const char *url,
        }
 
        ildb->rootDSE = NULL;
+       ildb->ldb     = ldb;
 
        ildb->ldap = ldap_new_connection(ildb, ldb_get_opaque(ldb, "EventContext"));
        if (!ildb->ldap) {
@@ -474,8 +500,19 @@ int ildb_connect(struct ldb_context *ldb, const char *url,
        ldb->modules->private_data = ildb;
        ldb->modules->ops = &ildb_ops;
 
-       if (cmdline_credentials != NULL && cli_credentials_authentication_requested(cmdline_credentials)) {
-               status = ldap_bind_sasl(ildb->ldap, cmdline_credentials);
+       /* caller can optionally setup credentials using the opaque token 'credentials' */
+       creds = talloc_get_type(ldb_get_opaque(ldb, "credentials"), struct cli_credentials);
+       if (creds == NULL) {
+               struct auth_session_info *session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info);
+               if (session_info && session_info->credentials) {
+                       creds = session_info->credentials;
+               } else {
+                       creds = cmdline_credentials;
+               }
+       }
+
+       if (creds != NULL && cli_credentials_authentication_requested(creds)) {
+               status = ldap_bind_sasl(ildb->ldap, creds);
                if (!NT_STATUS_IS_OK(status)) {
                        ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n",
                                  ldap_errstr(ildb->ldap, status));