s4/rodc: Support read-only database
authorAnatoliy Atanasov <anatoliy.atanasov@postpath.com>
Tue, 4 May 2010 09:49:18 +0000 (11:49 +0200)
committerAnatoliy Atanasov <anatoliy.atanasov@postpath.com>
Tue, 4 May 2010 16:32:18 +0000 (18:32 +0200)
Check on modify if we are RODC and return referral.
On the ldap backend side now we pass context and ldb_modify_default_callback
to propagate the referral error to the client.

source4/dsdb/common/util.c
source4/dsdb/samdb/ldb_modules/partition.c
source4/dsdb/samdb/ldb_modules/repl_meta_data.c
source4/dsdb/samdb/ldb_modules/schema_data.c
source4/dsdb/samdb/ldb_modules/util.c
source4/ldap_server/ldap_backend.c
source4/lib/ldb/common/ldb.c
source4/lib/ldb/include/ldb.h

index 771d30a4cddbc2fafe92937a759add85bb5cc6a3..7064fcf67d44624da82b0a8a6ff17354026e1bc1 100644 (file)
@@ -2751,6 +2751,7 @@ int samdb_is_rodc(struct ldb_context *sam_ctx, const struct GUID *objectGUID, bo
        if (ret != LDB_SUCCESS) {
                DEBUG(1,(("Failed to find our own NTDS Settings object by objectGUID=%s!\n"),
                         GUID_string(tmp_ctx, objectGUID)));
+               *is_rodc = false;
                talloc_free(tmp_ctx);
                return ret;
        }
index 19bd03633a8b1340623b475ffa11a76bab109f50..6c0d9cdd4ed1bb6937102d45e86882d67395d017 100644 (file)
@@ -167,8 +167,7 @@ static int partition_req_callback(struct ldb_request *req,
 
        switch (ares->type) {
        case LDB_REPLY_REFERRAL:
-               /* ignore referrals for now */
-               break;
+               return ldb_module_send_referral(ac->req, ares->referral);
 
        case LDB_REPLY_ENTRY:
                if (ac->req->operation != LDB_SEARCH) {
index 11e043f5d0931b9639f84c1e39b46628d083327e..374467cb94dc2c7b155b711c3f582e3ebd5b09db 100644 (file)
@@ -1076,6 +1076,7 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
  */
 static int replmd_update_rpmd(struct ldb_module *module, 
                              const struct dsdb_schema *schema, 
+                             struct ldb_request *req,
                              struct ldb_message *msg, uint64_t *seq_num,
                              time_t t,
                              bool *is_urgent)
@@ -1092,6 +1093,7 @@ static int replmd_update_rpmd(struct ldb_module *module,
        struct ldb_context *ldb;
        struct ldb_message_element *objectclass_el;
        enum urgent_situation situation;
+       bool rodc;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -1157,6 +1159,20 @@ static int replmd_update_rpmd(struct ldb_module *module,
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
+       /*we have elements that will be modified*/
+       if (msg->num_elements > 0) {
+               /*if we are RODC and this is a DRSR update then its ok*/
+               if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
+                       ret = samdb_rodc(ldb, &rodc);
+                       if (ret != LDB_SUCCESS) {
+                               DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
+                       } else if (rodc) {
+                               ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n");
+                               return LDB_ERR_REFERRAL;
+                       }
+               }
+       }
+
        for (i=0; i<msg->num_elements; i++) {
                struct ldb_message_element *old_el;
                old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
@@ -2043,6 +2059,8 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
        time_t t = time(NULL);
        int ret;
        bool is_urgent = false;
+       struct loadparm_context *lp_ctx;
+       char *referral;
 
        /* do not manipulate our control entries */
        if (ldb_dn_is_special(req->op.mod.message->dn)) {
@@ -2050,6 +2068,8 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
        }
 
        ldb = ldb_module_get_ctx(module);
+       lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+                                struct loadparm_context);
 
        ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
 
@@ -2069,7 +2089,18 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
        ldb_msg_remove_attr(msg, "whenChanged");
        ldb_msg_remove_attr(msg, "uSNChanged");
 
-       ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t, &is_urgent);
+       ret = replmd_update_rpmd(module, ac->schema, req, msg, &ac->seq_num, t, &is_urgent);
+       if (ret == LDB_ERR_REFERRAL) {
+               talloc_free(ac);
+
+               referral = talloc_asprintf(req,
+                                          "ldap://%s/%s",
+                                          lp_dnsdomain(lp_ctx),
+                                          ldb_dn_get_linearized(msg->dn));
+               ret = ldb_module_send_referral(req, referral);
+               return ldb_module_done(req, NULL, NULL, ret);
+       }
+
        if (ret != LDB_SUCCESS) {
                talloc_free(ac);
                return ret;
index 655b4892af39342386a5cdfdb27ab825922155c7..a6487441c1cddd6251279299207cbeeb4f59fea5 100644 (file)
@@ -141,6 +141,8 @@ static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
        const char *oid_attr = NULL;
        const char *oid = NULL;
        WERROR status;
+       bool rodc;
+       int ret;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -159,7 +161,12 @@ static int schema_data_add(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
-       if (!schema->fsmo.we_are_master) {
+       ret = samdb_rodc(ldb, &rodc);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(4, (__location__ ": unable to tell if we are an RODC \n"));
+       }
+
+       if (!schema->fsmo.we_are_master && !rodc) {
                ldb_debug_set(ldb, LDB_DEBUG_ERROR,
                          "schema_data_add: we are not master: reject request\n");
                return LDB_ERR_UNWILLING_TO_PERFORM;
index 18631c4350343f300fae7d0acffe7f6dd64b3898..c233df7216533671b56bb07c0dfdd63494578989 100644 (file)
@@ -243,12 +243,18 @@ int dsdb_module_modify(struct ldb_module *module,
        int ret;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
        ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
                                message,
                                NULL,
-                               NULL,
-                               ldb_op_default_callback,
+                               res,
+                               ldb_modify_default_callback,
                                NULL);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -292,13 +298,19 @@ int dsdb_module_rename(struct ldb_module *module,
        int ret;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
        ret = ldb_build_rename_req(&req, ldb, tmp_ctx,
                                   olddn,
                                   newdn,
                                   NULL,
-                                  NULL,
-                                  ldb_op_default_callback,
+                                  res,
+                                  ldb_modify_default_callback,
                                   NULL);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -340,12 +352,18 @@ int dsdb_module_add(struct ldb_module *module,
        int ret;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
        ret = ldb_build_add_req(&req, ldb, tmp_ctx,
                                message,
                                NULL,
-                               NULL,
-                               ldb_op_default_callback,
+                               res,
+                               ldb_modify_default_callback,
                                NULL);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -717,6 +735,7 @@ int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
        struct ldb_message *msg;
        struct dsdb_control_current_partition *p_ctrl;
        int ret;
+       struct ldb_result *res;
 
        msg = ldb_msg_new(module);
        if (msg == NULL) {
@@ -729,6 +748,11 @@ int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
+       res = talloc_zero(msg, struct ldb_result);
+       if (!res) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
        ret = ldb_msg_add_fmt(msg, "uSNHighest", "%llu", (unsigned long long)uSN);
        if (ret != LDB_SUCCESS) {
                talloc_free(msg);
@@ -754,11 +778,11 @@ int dsdb_module_save_partition_usn(struct ldb_module *module, struct ldb_dn *dn,
        }
        p_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
        p_ctrl->dn = dn;
-
        ret = ldb_build_mod_req(&req, ldb, msg,
                                msg,
                                NULL,
-                               NULL, ldb_op_default_callback,
+                               res,
+                               ldb_modify_default_callback,
                                NULL);
 again:
        if (ret != LDB_SUCCESS) {
@@ -784,7 +808,8 @@ again:
                ret = ldb_build_add_req(&req, ldb, msg,
                                        msg,
                                        NULL,
-                                       NULL, ldb_op_default_callback,
+                                       res,
+                                       ldb_modify_default_callback,
                                        NULL);
                goto again;
        }
index 3e802a7b97e4bc2565d1b582e29d49b6f6c9441d..e6f6c66bcbbf5a231f2e1d2577974d7c051243b8 100644 (file)
@@ -174,7 +174,8 @@ static int map_ldb_error(TALLOC_CTX *mem_ctx, int ldb_err,
 /* create and execute a modify request */
 static int ldb_mod_req_with_controls(struct ldb_context *ldb,
                                     const struct ldb_message *message,
-                                    struct ldb_control **controls)
+                                    struct ldb_control **controls,
+                                    void *context)
 {
        struct ldb_request *req;
        int ret;
@@ -187,8 +188,8 @@ static int ldb_mod_req_with_controls(struct ldb_context *ldb,
        ret = ldb_build_mod_req(&req, ldb, ldb,
                                        message,
                                        controls,
-                                       NULL,
-                                       ldb_op_default_callback,
+                                       context,
+                                       ldb_modify_default_callback,
                                        NULL);
 
        if (ret != LDB_SUCCESS) {
@@ -315,6 +316,124 @@ static NTSTATUS ldapsrv_unwilling(struct ldapsrv_call *call, int error)
        return NT_STATUS_OK;
 }
 
+int ldb_add_with_context(struct ldb_context *ldb,
+                        const struct ldb_message *message,
+                        void *context)
+{
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_msg_sanity_check(ldb, message);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = ldb_build_add_req(&req, ldb, ldb,
+                                       message,
+                                       NULL,
+                                       context,
+                                       ldb_modify_default_callback,
+                                       NULL);
+
+       if (ret != LDB_SUCCESS) return ret;
+
+       ret = ldb_transaction_start(ldb);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = ldb_request(ldb, req);
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_transaction_commit(ldb);
+       }
+       else {
+               ldb_transaction_cancel(ldb);
+       }
+
+       talloc_free(req);
+       return ret;
+}
+
+int ldb_delete_with_context(struct ldb_context *ldb,
+                           struct ldb_dn *dn,
+                           void *context)
+{
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_build_del_req(&req, ldb, ldb,
+                                       dn,
+                                       NULL,
+                                       context,
+                                       ldb_modify_default_callback,
+                                       NULL);
+
+       if (ret != LDB_SUCCESS) return ret;
+
+       ret = ldb_transaction_start(ldb);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = ldb_request(ldb, req);
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_transaction_commit(ldb);
+       }
+       else {
+               ldb_transaction_cancel(ldb);
+       }
+
+       talloc_free(req);
+       return ret;
+}
+
+int ldb_rename_with_context(struct ldb_context *ldb,
+              struct ldb_dn *olddn,
+              struct ldb_dn *newdn,
+              void *context)
+{
+       struct ldb_request *req;
+       int ret;
+
+       ret = ldb_build_rename_req(&req, ldb, ldb,
+                                       olddn,
+                                       newdn,
+                                       NULL,
+                                       context,
+                                       ldb_modify_default_callback,
+                                       NULL);
+
+       if (ret != LDB_SUCCESS) return ret;
+
+       ret = ldb_transaction_start(ldb);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       ret = ldb_request(ldb, req);
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_transaction_commit(ldb);
+       }
+       else {
+               ldb_transaction_cancel(ldb);
+       }
+
+       talloc_free(req);
+       return ret;
+}
+
 static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call)
 {
        struct ldap_SearchRequest *req = &call->request->r.SearchRequest;
@@ -545,6 +664,7 @@ static NTSTATUS ldapsrv_ModifyRequest(struct ldapsrv_call *call)
        int result = LDAP_SUCCESS;
        int ldb_ret;
        unsigned int i,j;
+       struct ldb_result *res = NULL;
 
        DEBUG(10, ("ModifyRequest"));
        DEBUGADD(10, (" dn: %s", req->dn));
@@ -612,16 +732,24 @@ reply:
        NT_STATUS_HAVE_NO_MEMORY(modify_reply);
 
        if (result == LDAP_SUCCESS) {
-               ldb_ret = ldb_mod_req_with_controls(samdb, msg, call->request->controls);
+               res = talloc_zero(local_ctx, struct ldb_result);
+               NT_STATUS_HAVE_NO_MEMORY(res);
+               ldb_ret = ldb_mod_req_with_controls(samdb, msg, call->request->controls, res);
                result = map_ldb_error(local_ctx, ldb_ret, &errstr);
        }
 
        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;
 
+       if (res->refs != NULL) {
+               modify_result->resultcode = map_ldb_error(local_ctx, LDB_ERR_REFERRAL, &errstr);
+               modify_result->errormessage = (errstr?talloc_strdup(modify_reply, errstr):NULL);
+               modify_result->referral = talloc_strdup(call, *res->refs);
+       } else {
+               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);
@@ -642,6 +770,7 @@ static NTSTATUS ldapsrv_AddRequest(struct ldapsrv_call *call)
        int result = LDAP_SUCCESS;
        int ldb_ret;
        unsigned int i,j;
+       struct ldb_result *res = NULL;
 
        DEBUG(10, ("AddRequest"));
        DEBUGADD(10, (" dn: %s", req->dn));
@@ -691,16 +820,23 @@ reply:
        NT_STATUS_HAVE_NO_MEMORY(add_reply);
 
        if (result == LDAP_SUCCESS) {
-               ldb_ret = ldb_add(samdb, msg);
+               res = talloc_zero(local_ctx, struct ldb_result);
+               NT_STATUS_HAVE_NO_MEMORY(res);
+               ldb_ret = ldb_add_with_context(samdb, msg, res);
                result = map_ldb_error(local_ctx, ldb_ret, &errstr);
        }
 
        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;
-
+       if (res->refs != NULL) {
+               add_result->resultcode =  map_ldb_error(local_ctx, LDB_ERR_REFERRAL, &errstr);
+               add_result->errormessage = (errstr?talloc_strdup(add_reply,errstr):NULL);
+               add_result->referral = talloc_strdup(call, *res->refs);
+       } else {
+               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);
@@ -719,6 +855,7 @@ static NTSTATUS ldapsrv_DelRequest(struct ldapsrv_call *call)
        const char *errstr = NULL;
        int result = LDAP_SUCCESS;
        int ldb_ret;
+       struct ldb_result *res = NULL;
 
        DEBUG(10, ("DelRequest"));
        DEBUGADD(10, (" dn: %s", req->dn));
@@ -736,15 +873,23 @@ reply:
        NT_STATUS_HAVE_NO_MEMORY(del_reply);
 
        if (result == LDAP_SUCCESS) {
-               ldb_ret = ldb_delete(samdb, dn);
+               res = talloc_zero(local_ctx, struct ldb_result);
+               NT_STATUS_HAVE_NO_MEMORY(res);
+               ldb_ret = ldb_delete_with_context(samdb, dn, res);
                result = map_ldb_error(local_ctx, ldb_ret, &errstr);
        }
 
        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;
+       if (res->refs != NULL) {
+               del_result->resultcode = map_ldb_error(local_ctx, LDB_ERR_REFERRAL, &errstr);
+               del_result->errormessage = (errstr?talloc_strdup(del_reply,errstr):NULL);
+               del_result->referral = talloc_strdup(call, *res->refs);
+       } else {
+               del_result->resultcode = result;
+               del_result->errormessage = (errstr?talloc_strdup(del_reply,errstr):NULL);
+               del_result->referral = NULL;
+       }
 
        talloc_free(local_ctx);
 
@@ -764,6 +909,7 @@ static NTSTATUS ldapsrv_ModifyDNRequest(struct ldapsrv_call *call)
        const char *errstr = NULL;
        int result = LDAP_SUCCESS;
        int ldb_ret;
+       struct ldb_result *res = NULL;
 
        DEBUG(10, ("ModifyDNRequest"));
        DEBUGADD(10, (" dn: %s", req->dn));
@@ -827,15 +973,23 @@ reply:
        NT_STATUS_HAVE_NO_MEMORY(modifydn_r);
 
        if (result == LDAP_SUCCESS) {
-               ldb_ret = ldb_rename(samdb, olddn, newdn);
+               res = talloc_zero(local_ctx, struct ldb_result);
+               NT_STATUS_HAVE_NO_MEMORY(res);
+               ldb_ret = ldb_rename_with_context(samdb, olddn, newdn, res);
                result = map_ldb_error(local_ctx, 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;
+       if (res->refs != NULL) {
+               modifydn->resultcode = map_ldb_error(local_ctx, LDB_ERR_REFERRAL, &errstr);;
+               modifydn->errormessage = (errstr?talloc_strdup(modifydn_r,errstr):NULL);
+               modifydn->referral = talloc_strdup(call, *res->refs);
+       } else {
+               modifydn->resultcode = result;
+               modifydn->errormessage = (errstr?talloc_strdup(modifydn_r,errstr):NULL);
+               modifydn->referral = NULL;
+       }
 
        talloc_free(local_ctx);
 
index bbb3b79e6cc7b201796c682ef91a95847a5eaa48..07aa6d0985f9aaa44c33e9745d929042af139af5 100644 (file)
@@ -903,6 +903,54 @@ int ldb_search_default_callback(struct ldb_request *req,
        return LDB_SUCCESS;
 }
 
+int ldb_modify_default_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct ldb_result *res;
+       unsigned int n;
+       int ret;
+
+       res = talloc_get_type(req->context, struct ldb_result);
+
+       if (!ares) {
+               return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       if (ares->error != LDB_SUCCESS) {
+               ret = ares->error;
+               talloc_free(ares);
+               return ldb_request_done(req, ret);
+       }
+
+       switch (ares->type) {
+       case LDB_REPLY_REFERRAL:
+               if (res->refs) {
+                       for (n = 0; res->refs[n]; n++) /*noop*/ ;
+               } else {
+                       n = 0;
+               }
+
+               res->refs = talloc_realloc(res, res->refs, char *, n + 2);
+               if (! res->refs) {
+                       return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+               }
+
+               res->refs[n] = talloc_move(res->refs, &ares->referral);
+               res->refs[n + 1] = NULL;
+               break;
+
+       case LDB_REPLY_DONE:
+               talloc_free(ares);
+               return ldb_request_done(req, LDB_SUCCESS);
+       default:
+               talloc_free(ares);
+               ldb_set_errstring(req->handle->ldb, "Invalid reply type!");
+               return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       talloc_free(ares);
+       return ldb_request_done(req, LDB_SUCCESS);
+}
+
 int ldb_op_default_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        int ret;
index fc5d47ac32187fbbca21ac23f0a5600d5316c759..b644b995ac974280c46ce1919c07da11a9b6cd17 100644 (file)
@@ -1017,6 +1017,7 @@ int ldb_search_default_callback(struct ldb_request *req, struct ldb_reply *ares)
 */
 int ldb_op_default_callback(struct ldb_request *req, struct ldb_reply *ares);
 
+int ldb_modify_default_callback(struct ldb_request *req, struct ldb_reply *ares);
 
 /**
   Helper function to build a search request