s4:partition DSDB module - Generate basic referrals
authorMatthias Dieter Wallnöfer <mwallnoefer@yahoo.de>
Sat, 20 Feb 2010 21:07:12 +0000 (22:07 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 24 Feb 2010 21:17:06 +0000 (08:17 +1100)
This is a first, very basic implementation of the referrals (more informations
at MS-ADTS 3.1.1.4.6 and 3.1.1.3.4.1.12).

To have the full referral support (and to always point to the right host) the
full implementation using DNS will be needed (at the moment we always point to
the main DC which is referenceable through the DNS domainname).

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
source4/dsdb/samdb/ldb_modules/partition.c
source4/dsdb/samdb/ldb_modules/partition.h

index 1d4717b8fdc003426ae2e4af17901574e392147a..9d59b5437d32de78b19979a8d0f3f46e97e0b03d 100644 (file)
@@ -43,6 +43,8 @@ struct partition_context {
        struct part_request *part_req;
        int num_requests;
        int finished_requests;
+
+       const char **referrals;
 };
 
 static struct partition_context *partition_init_ctx(struct ldb_module *module, struct ldb_request *req)
@@ -197,6 +199,19 @@ static int partition_req_callback(struct ldb_request *req,
 
                ac->finished_requests++;
                if (ac->finished_requests == ac->num_requests) {
+                       /* Send back referrals if they do exist (search ops) */
+                       if (ac->referrals != NULL) {
+                               const char **ref;
+                               for (ref = ac->referrals; *ref != NULL; ++ref) {
+                                       ret = ldb_module_send_referral(ac->req,
+                                                                      talloc_strdup(ac->req, *ref));
+                                       if (ret != LDB_SUCCESS) {
+                                               return ldb_module_done(ac->req, NULL, NULL,
+                                                                      ret);
+                                       }
+                               }
+                       }
+
                        /* this was the last one, call callback */
                        return ldb_module_done(ac->req, ares->controls,
                                               ares->response, 
@@ -434,23 +449,21 @@ static int partition_replicate(struct ldb_module *module, struct ldb_request *re
 /* search */
 static int partition_search(struct ldb_module *module, struct ldb_request *req)
 {
-       int ret;
        struct ldb_control **saved_controls;
        /* Find backend */
        struct partition_private_data *data = talloc_get_type(module->private_data, 
                                                              struct partition_private_data);
-
-       /* issue request */
-
-       /* (later) consider if we should be searching multiple
-        * partitions (for 'invisible' partition behaviour */
+       struct partition_context *ac;
+       struct ldb_context *ldb;
+       struct loadparm_context *lp_ctx;
 
        struct ldb_control *search_control = ldb_request_get_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID);
        struct ldb_control *domain_scope_control = ldb_request_get_control(req, LDB_CONTROL_DOMAIN_SCOPE_OID);
        
        struct ldb_search_options_control *search_options = NULL;
        struct dsdb_partition *p;
-
+       unsigned int i, j;
+       int ret;
        bool domain_scope = false, phantom_root = false;
        
        ret = partition_reload_if_required(module, data);
@@ -496,66 +509,149 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req)
                        & ~LDB_SEARCH_OPTION_PHANTOM_ROOT;
        }
 
-       if ((!domain_scope) || phantom_root) {
-               int i;
-               struct partition_context *ac;
+       if (!data || !data->partitions) {
+               return ldb_next_request(module, req);
+       }
+
+       ac = partition_init_ctx(module, req);
+       if (!ac) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
-               ac = partition_init_ctx(module, req);
-               if (!ac) {
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
+       ldb = ldb_module_get_ctx(ac->module);
+       lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+                                               struct loadparm_context);
 
-               /* Search from the base DN */
-               if (!req->op.search.base || ldb_dn_is_null(req->op.search.base)) {
-                       return partition_send_all(module, ac, req);
-               }
-               for (i=0; data && data->partitions && data->partitions[i]; i++) {
-                       bool match = false, stop = false;
-                       /* Find all partitions under the search base 
-                          
-                          we match if:
-
-                             1) the DN we are looking for exactly matches the partition
-                            or
-                             2) the DN we are looking for is a parent of the partition and it isn't
-                                 a scope base search
-                             or
-                             3) the DN we are looking for is a child of the partition
+       /* Search from the base DN */
+       if (ldb_dn_is_null(req->op.search.base)) {
+               return partition_send_all(module, ac, req);
+       }
+
+       for (i=0; data->partitions[i]; i++) {
+               bool match = false, stop = false;
+
+               if (phantom_root) {
+                       /* Phantom root: Find all partitions under the
+                        * search base. We match if:
+                        *
+                        * 1) the DN we are looking for exactly matches a
+                        *    certain partition and always stop
+                        * 2) the DN we are looking for is a parent of certain
+                        *    partitions and it isn't a scope base search
+                        * 3) the DN we are looking for is a child of a certain
+                        *    partition and always stop
+                        *    - we don't need to go any further up in the
+                        *    hierarchy!
                         */
-                       if (ldb_dn_compare(data->partitions[i]->ctrl->dn, req->op.search.base) == 0) {
+                       if (ldb_dn_compare(data->partitions[i]->ctrl->dn,
+                                          req->op.search.base) == 0) {
                                match = true;
                                stop = true;
                        }
-                       if (!match && 
-                           (ldb_dn_compare_base(req->op.search.base, data->partitions[i]->ctrl->dn) == 0 &&
+                       if (!match &&
+                           (ldb_dn_compare_base(req->op.search.base,
+                                                data->partitions[i]->ctrl->dn) == 0 &&
                             req->op.search.scope != LDB_SCOPE_BASE)) {
                                match = true;
                        }
                        if (!match &&
-                           ldb_dn_compare_base(data->partitions[i]->ctrl->dn, req->op.search.base) == 0) {
+                           ldb_dn_compare_base(data->partitions[i]->ctrl->dn,
+                                               req->op.search.base) == 0) {
                                match = true;
                                stop = true; /* note that this relies on partition ordering */
                        }
-                       if (match) {
-                               ret = partition_prep_request(ac, data->partitions[i]);
-                               if (ret != LDB_SUCCESS) {
-                                       return ret;
+               } else {
+                       /* Domain scope: Find all partitions under the search
+                        * base.
+                        *
+                        * We generate referral candidates if we haven't
+                        * specified the domain scope control, haven't a base
+                        * search* scope and the DN we are looking for is a real
+                        * predecessor of certain partitions. When a new
+                        * referral candidate is nearer to the DN than an
+                        * existing one delete the latter (we want to have only
+                        * the closest ones). When we checked this for all
+                        * candidates we have the final referrals.
+                        *
+                        * We match if the DN we are looking for is a child of
+                        * a certain partition or the partition
+                        * DN itself - we don't need to go any further
+                        * up in the hierarchy!
+                        */
+                       if ((!domain_scope) &&
+                           (req->op.search.scope != LDB_SCOPE_BASE) &&
+                           (ldb_dn_compare_base(req->op.search.base,
+                                                data->partitions[i]->ctrl->dn) == 0) &&
+                           (ldb_dn_compare(req->op.search.base,
+                                           data->partitions[i]->ctrl->dn) != 0)) {
+                               char *ref = talloc_asprintf(ac,
+                                                           "ldap://%s/%s%s",
+                                                           lp_dnsdomain(lp_ctx),
+                                                           ldb_dn_get_linearized(data->partitions[i]->ctrl->dn),
+                                                           req->op.search.scope == LDB_SCOPE_ONELEVEL ? "??base" : "");
+
+                               if (ref == NULL) {
+                                       ldb_oom(ldb);
+                                       return LDB_ERR_OPERATIONS_ERROR;
+                               }
+
+                               /* Initialise the referrals list */
+                               if (ac->referrals == NULL) {
+                                       ac->referrals = (const char **) str_list_make_empty(ac);
+                                       if (ac->referrals == NULL) {
+                                               ldb_oom(ldb);
+                                               return LDB_ERR_OPERATIONS_ERROR;
+                                       }
+                               }
+
+                               /* Check if the new referral candidate is
+                                * closer to the base DN than already
+                                * saved ones and delete the latters */
+                               j = 0;
+                               while (ac->referrals[j] != NULL) {
+                                       if (strstr(ac->referrals[j],
+                                                  ldb_dn_get_linearized(data->partitions[i]->ctrl->dn)) != NULL) {
+                                               str_list_remove(ac->referrals,
+                                                               ac->referrals[j]);
+                                       } else {
+                                               ++j;
+                                       }
+                               }
+
+                               /* Add our new candidate */
+                               ac->referrals = str_list_add(ac->referrals, ref);
+
+                               talloc_free(ref);
+
+                               if (ac->referrals == NULL) {
+                                       ldb_oom(ldb);
+                                       return LDB_ERR_OPERATIONS_ERROR;
                                }
                        }
-                       if (stop) break;
+                       if (ldb_dn_compare_base(data->partitions[i]->ctrl->dn, req->op.search.base) == 0) {
+                               match = true;
+                               stop = true; /* note that this relies on partition ordering */
+                       }
                }
 
-               /* Perhaps we didn't match any partitions.  Try the main partition, only */
-               if (ac->num_requests == 0) {
-                       talloc_free(ac);
-                       return ldb_next_request(module, req);
+               if (match) {
+                       ret = partition_prep_request(ac, data->partitions[i]);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
                }
 
-               /* fire the first one */
-               return partition_call_first(ac);
-       } else {
-               return partition_replicate(module, req, req->op.search.base);
+               if (stop) break;
        }
+
+       /* Perhaps we didn't match any partitions. Try the main partition */
+       if (ac->num_requests == 0) {
+               talloc_free(ac);
+               return ldb_next_request(module, req);
+       }
+
+       /* fire the first one */
+       return partition_call_first(ac);
 }
 
 /* add */
index 1d4e8e44fa647a65456b89ea10b783f98e75e2eb..594634cc99032522d797dddca28aca42981c7b04 100644 (file)
@@ -25,6 +25,7 @@
 #include "dsdb/samdb/samdb.h"
 #include "dsdb/samdb/ldb_modules/util.h"
 #include "system/locale.h"
+#include "param/param.h"
 
 struct dsdb_partition {
        struct ldb_module *module;