LCOV - code coverage report
Current view: top level - source4/dsdb/samdb/ldb_modules - tombstone_reanimate.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 125 161 77.6 %
Date: 2024-05-31 13:13:24 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :    ldb database library
       3             : 
       4             :    Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2014
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : /*
      21             :  *  Name: tombstone_reanimate
      22             :  *
      23             :  *  Component: Handle Tombstone reanimation requests
      24             :  *
      25             :  *  Description:
      26             :  *      Tombstone reanimation requests are plain ldap modify request like:
      27             :  *        dn: CN=tombi 1\0ADEL:e6e17ff7-8986-4cdd-87ad-afb683ccbb89,CN=Deleted Objects,DC=samba4,DC=devel
      28             :  *        changetype: modify
      29             :  *        delete: isDeleted
      30             :  *        -
      31             :  *        replace: distinguishedName
      32             :  *        distinguishedName: CN=Tombi 1,CN=Users,DC=samba4,DC=devel
      33             :  *        -
      34             :  *
      35             :  *      Usually we don't allow distinguishedName modifications (see rdn_name.c)
      36             :  *      Reanimating Tombstones is described here:
      37             :  *        - http://msdn.microsoft.com/en-us/library/cc223467.aspx
      38             :  *
      39             :  *  Author: Kamen Mazdrashki
      40             :  */
      41             : 
      42             : 
      43             : #include "includes.h"
      44             : #include "ldb_module.h"
      45             : #include "dsdb/samdb/samdb.h"
      46             : #include "librpc/ndr/libndr.h"
      47             : #include "librpc/gen_ndr/ndr_security.h"
      48             : #include "libcli/security/security.h"
      49             : #include "auth/auth.h"
      50             : #include "param/param.h"
      51             : #include "../libds/common/flags.h"
      52             : #include "dsdb/samdb/ldb_modules/util.h"
      53             : #include "libds/common/flag_mapping.h"
      54             : 
      55             : struct tr_context {
      56             :         struct ldb_module *module;
      57             : 
      58             :         struct ldb_request *req;
      59             :         const struct ldb_message *req_msg;
      60             : 
      61             :         struct ldb_result *search_res;
      62             :         const struct ldb_message *search_msg;
      63             : 
      64             :         struct ldb_message *mod_msg;
      65             :         struct ldb_result *mod_res;
      66             :         struct ldb_request *mod_req;
      67             : 
      68             :         struct ldb_dn *rename_dn;
      69             :         struct ldb_result *rename_res;
      70             :         struct ldb_request *rename_req;
      71             : 
      72             :         const struct dsdb_schema *schema;
      73             : };
      74             : 
      75         274 : static struct tr_context *tr_init_context(struct ldb_module *module,
      76             :                                           struct ldb_request *req)
      77             : {
      78         274 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
      79           0 :         struct tr_context *ac;
      80             : 
      81         274 :         ac = talloc_zero(req, struct tr_context);
      82         274 :         if (ac == NULL) {
      83           0 :                 ldb_oom(ldb);
      84           0 :                 return NULL;
      85             :         }
      86             : 
      87         274 :         ac->module = module;
      88         274 :         ac->req = req;
      89         274 :         ac->req_msg = req->op.mod.message;
      90         274 :         ac->schema = dsdb_get_schema(ldb, ac);
      91             : 
      92         274 :         return ac;
      93             : }
      94             : 
      95             : 
      96      321088 : static bool is_tombstone_reanimate_request(struct ldb_request *req,
      97             :                                            const struct ldb_message_element **pel_dn)
      98             : {
      99       17163 :         struct ldb_message_element *el_dn;
     100       17163 :         struct ldb_message_element *el_deleted;
     101             : 
     102             :         /* check distinguishedName requirement */
     103      321088 :         el_dn = ldb_msg_find_element(req->op.mod.message, "distinguishedName");
     104      321088 :         if (el_dn == NULL) {
     105      303648 :                 return false;
     106             :         }
     107         277 :         if (LDB_FLAG_MOD_TYPE(el_dn->flags) != LDB_FLAG_MOD_REPLACE) {
     108           2 :                 return false;
     109             :         }
     110         275 :         if (el_dn->num_values != 1) {
     111           0 :                 return false;
     112             :         }
     113             : 
     114             :         /* check isDeleted requirement */
     115         275 :         el_deleted = ldb_msg_find_element(req->op.mod.message, "isDeleted");
     116         275 :         if (el_deleted == NULL) {
     117           1 :                 return false;
     118             :         }
     119             : 
     120         274 :         if (LDB_FLAG_MOD_TYPE(el_deleted->flags) != LDB_FLAG_MOD_DELETE) {
     121           0 :                 return false;
     122             :         }
     123             : 
     124         274 :         *pel_dn = el_dn;
     125         274 :         return true;
     126             : }
     127             : 
     128             : /**
     129             :  * Local rename implementation based on dsdb_module_rename()
     130             :  * so we could fine tune it and add more controls
     131             :  */
     132         274 : static int tr_prepare_rename(struct tr_context *ac,
     133             :                              const struct ldb_message_element *new_dn)
     134             : {
     135         274 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     136           0 :         int ret;
     137             : 
     138         274 :         ac->rename_dn = ldb_dn_from_ldb_val(ac, ldb, &new_dn->values[0]);
     139         274 :         if (ac->rename_dn == NULL) {
     140           0 :                 return ldb_module_oom(ac->module);
     141             :         }
     142             : 
     143         274 :         ac->rename_res = talloc_zero(ac, struct ldb_result);
     144         274 :         if (ac->rename_res == NULL) {
     145           0 :                 return ldb_module_oom(ac->module);
     146             :         }
     147             : 
     148         274 :         ret = ldb_build_rename_req(&ac->rename_req, ldb, ac,
     149         274 :                                    ac->req_msg->dn,
     150             :                                    ac->rename_dn,
     151             :                                    NULL,
     152         274 :                                    ac->rename_res,
     153             :                                    ldb_modify_default_callback,
     154             :                                    ac->req);
     155         274 :         LDB_REQ_SET_LOCATION(ac->rename_req);
     156         274 :         if (ret != LDB_SUCCESS) {
     157           0 :                 return ret;
     158             :         }
     159             : 
     160         274 :         return ret;
     161             : }
     162             : 
     163             : /**
     164             :  * Local rename implementation based on dsdb_module_modify()
     165             :  * so we could fine tune it and add more controls
     166             :  */
     167         539 : static int tr_do_down_req(struct tr_context *ac, struct ldb_request *down_req)
     168             : {
     169           0 :         int ret;
     170             : 
     171             :         /* We need this since object is 'delete' atm */
     172         539 :         ret = ldb_request_add_control(down_req,
     173             :                                       LDB_CONTROL_SHOW_DELETED_OID,
     174             :                                       false, NULL);
     175         539 :         if (ret != LDB_SUCCESS) {
     176           0 :                 return ret;
     177             :         }
     178             : 
     179             :         /* mark request as part of Tombstone reanimation */
     180         539 :         ret = ldb_request_add_control(down_req,
     181             :                                       DSDB_CONTROL_RESTORE_TOMBSTONE_OID,
     182             :                                       false, NULL);
     183         539 :         if (ret != LDB_SUCCESS) {
     184           0 :                 return ret;
     185             :         }
     186             : 
     187             :         /* Run request from Next module */
     188         539 :         ret = ldb_next_request(ac->module, down_req);
     189         539 :         if (ret == LDB_SUCCESS) {
     190         503 :                 ret = ldb_wait(down_req->handle, LDB_WAIT_ALL);
     191             :         }
     192             : 
     193         539 :         return ret;
     194             : }
     195             : 
     196         274 : static int tr_prepare_attributes(struct tr_context *ac)
     197             : {
     198         274 :         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
     199           0 :         int ret;
     200         274 :         struct ldb_message_element *el = NULL;
     201           0 :         uint32_t account_type, user_account_control;
     202         274 :         struct ldb_dn *objectcategory = NULL;
     203             : 
     204         274 :         ac->mod_msg = ldb_msg_copy_shallow(ac, ac->req_msg);
     205         274 :         if (ac->mod_msg == NULL) {
     206           0 :                 return ldb_oom(ldb);
     207             :         }
     208             : 
     209         274 :         ac->mod_res = talloc_zero(ac, struct ldb_result);
     210         274 :         if (ac->mod_res == NULL) {
     211           0 :                 return ldb_oom(ldb);
     212             :         }
     213             : 
     214         274 :         ret = ldb_build_mod_req(&ac->mod_req, ldb, ac,
     215         274 :                                 ac->mod_msg,
     216             :                                 NULL,
     217         274 :                                 ac->mod_res,
     218             :                                 ldb_modify_default_callback,
     219             :                                 ac->req);
     220         274 :         LDB_REQ_SET_LOCATION(ac->mod_req);
     221         274 :         if (ret != LDB_SUCCESS) {
     222           0 :                 return ret;
     223             :         }
     224             : 
     225             :         /* - remove distinguishedName - we don't need it */
     226         274 :         ldb_msg_remove_attr(ac->mod_msg, "distinguishedName");
     227             : 
     228             :         /* remove isRecycled */
     229         274 :         ret = ldb_msg_add_empty(ac->mod_msg, "isRecycled",
     230             :                                 LDB_FLAG_MOD_DELETE, NULL);
     231         274 :         if (ret != LDB_SUCCESS) {
     232           0 :                 ldb_asprintf_errstring(ldb, "Failed to reset isRecycled attribute: %s", ldb_strerror(ret));
     233           0 :                 return LDB_ERR_OPERATIONS_ERROR;
     234             :         }
     235             : 
     236             :         /* objectClass is USER */
     237         274 :         if (samdb_find_attribute(ldb, ac->search_msg, "objectclass", "user") != NULL) {
     238           0 :                 uint32_t primary_group_rid;
     239             :                 /* restoring 'user' instance attribute is heavily borrowed from samldb.c */
     240             : 
     241             :                 /* Default values */
     242          60 :                 ret = dsdb_user_obj_set_defaults(ldb, ac->mod_msg, ac->mod_req);
     243          60 :                 if (ret != LDB_SUCCESS) return ret;
     244             : 
     245             :                 /* "userAccountControl" must exists on deleted object */
     246          60 :                 user_account_control = ldb_msg_find_attr_as_uint(ac->search_msg,
     247             :                                                         "userAccountControl",
     248             :                                                         (uint32_t)-1);
     249          60 :                 if (user_account_control == (uint32_t)-1) {
     250           0 :                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
     251             :                                          "reanimate: No 'userAccountControl' attribute found!");
     252             :                 }
     253             : 
     254             :                 /* restore "sAMAccountType" */
     255          60 :                 ret = dsdb_user_obj_set_account_type(ldb, ac->mod_msg,
     256             :                                                      user_account_control, NULL);
     257          60 :                 if (ret != LDB_SUCCESS) {
     258           0 :                         return ret;
     259             :                 }
     260             : 
     261             :                 /* "userAccountControl" -> "primaryGroupID" mapping */
     262          60 :                 ret = dsdb_user_obj_set_primary_group_id(ldb, ac->mod_msg,
     263             :                                                          user_account_control,
     264             :                                                          &primary_group_rid);
     265          60 :                 if (ret != LDB_SUCCESS) {
     266           0 :                         return ret;
     267             :                 }
     268             :                 /*
     269             :                  * Older AD deployments don't know about the
     270             :                  * RODC group
     271             :                  */
     272          60 :                 if (primary_group_rid == DOMAIN_RID_READONLY_DCS) {
     273             :                         /* TODO:  check group exists */
     274           0 :                 }
     275             : 
     276             :         }
     277             : 
     278             :         /* objectClass is GROUP */
     279         274 :         if (samdb_find_attribute(ldb, ac->search_msg, "objectclass", "group") != NULL) {
     280             :                 /* "groupType" -> "sAMAccountType" */
     281           0 :                 uint32_t group_type;
     282             : 
     283           2 :                 el = ldb_msg_find_element(ac->search_msg, "groupType");
     284           2 :                 if (el == NULL) {
     285           0 :                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
     286             :                                          "reanimate: Unexpected: missing groupType attribute.");
     287             :                 }
     288             : 
     289           2 :                 group_type = ldb_msg_find_attr_as_uint(ac->search_msg,
     290             :                                                        "groupType", 0);
     291             : 
     292           2 :                 account_type = ds_gtype2atype(group_type);
     293           2 :                 if (account_type == 0) {
     294           0 :                         return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
     295             :                                          "reanimate: Unrecognized account type!");
     296             :                 }
     297           2 :                 ret = samdb_msg_append_uint(ldb, ac->mod_msg, ac->mod_msg,
     298             :                                             "sAMAccountType", account_type,
     299             :                                             LDB_FLAG_MOD_REPLACE);
     300           2 :                 if (ret != LDB_SUCCESS) {
     301           0 :                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
     302             :                                          "reanimate: Failed to add sAMAccountType to restored object.");
     303             :                 }
     304             : 
     305             :                 /* Default values set by Windows */
     306           2 :                 ret = samdb_find_or_add_attribute(ldb, ac->mod_msg,
     307             :                                                   "adminCount", "0");
     308           2 :                 if (ret != LDB_SUCCESS) return ret;
     309           2 :                 ret = samdb_find_or_add_attribute(ldb, ac->mod_msg,
     310             :                                                   "operatorCount", "0");
     311           2 :                 if (ret != LDB_SUCCESS) return ret;
     312             :         }
     313             : 
     314             :         /* - restore objectCategory if not present */
     315         274 :         objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, ac->search_msg,
     316             :                                                  "objectCategory");
     317         274 :         if (objectcategory == NULL) {
     318           0 :                 const char *value;
     319             : 
     320         274 :                 ret = dsdb_make_object_category(ldb, ac->schema, ac->search_msg,
     321         274 :                                                 ac->mod_msg, &value);
     322         274 :                 if (ret != LDB_SUCCESS) {
     323           0 :                         return ret;
     324             :                 }
     325             : 
     326         274 :                 ret = ldb_msg_append_string(ac->mod_msg, "objectCategory", value,
     327             :                                             LDB_FLAG_MOD_ADD);
     328         274 :                 if (ret != LDB_SUCCESS) {
     329           0 :                         return ret;
     330             :                 }
     331             :         }
     332             : 
     333         274 :         return LDB_SUCCESS;
     334             : }
     335             : 
     336             : /**
     337             :  * Handle special LDAP modify request to restore deleted objects
     338             :  */
     339      321803 : static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_request *req)
     340             : {
     341      321803 :         struct ldb_context *ldb = ldb_module_get_ctx(module);
     342      321803 :         const struct ldb_message_element *el_dn = NULL;
     343      321803 :         struct tr_context *ac = NULL;
     344       17221 :         int ret;
     345             : 
     346      321803 :         ldb_debug(ldb, LDB_DEBUG_TRACE, "%s\n", __PRETTY_FUNCTION__);
     347             : 
     348             :         /* do not manipulate our control entries */
     349      321803 :         if (ldb_dn_is_special(req->op.mod.message->dn)) {
     350         715 :                 return ldb_next_request(module, req);
     351             :         }
     352             : 
     353             :         /* Check if this is a reanimate request */
     354      321088 :         if (!is_tombstone_reanimate_request(req, &el_dn)) {
     355      320814 :                 return ldb_next_request(module, req);
     356             :         }
     357             : 
     358         274 :         ac = tr_init_context(module, req);
     359         274 :         if (ac == NULL) {
     360           0 :                 return ldb_operr(ldb);
     361             :         }
     362             : 
     363             :         /* Load original object */
     364         274 :         ret = dsdb_module_search_dn(module, ac, &ac->search_res,
     365         274 :                                     ac->req_msg->dn, NULL,
     366             :                                     DSDB_FLAG_TOP_MODULE |
     367             :                                     DSDB_SEARCH_SHOW_DELETED,
     368             :                                     req);
     369         274 :         if (ret != LDB_SUCCESS) {
     370           0 :                 return ldb_operr(ldb);
     371             :         }
     372         274 :         ac->search_msg = ac->search_res->msgs[0];
     373             : 
     374             :         /* check if it a Deleted Object */
     375         274 :         if (!ldb_msg_find_attr_as_bool(ac->search_msg, "isDeleted", false)) {
     376           0 :                 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, "Trying to restore not deleted object\n");
     377             :         }
     378             : 
     379             :         /* Simple implementation */
     380             : 
     381             :         /* prepare attributed depending on objectClass */
     382         274 :         ret = tr_prepare_attributes(ac);
     383         274 :         if (ret != LDB_SUCCESS) {
     384           0 :                 return ret;
     385             :         }
     386             : 
     387             :         /* Rename request to modify distinguishedName */
     388         274 :         ret = tr_prepare_rename(ac, el_dn);
     389         274 :         if (ret != LDB_SUCCESS) {
     390           0 :                 return ret;
     391             :         }
     392             : 
     393             :         /* restore attributed depending on objectClass */
     394         274 :         ret = tr_do_down_req(ac, ac->mod_req);
     395         274 :         if (ret != LDB_SUCCESS) {
     396           9 :                 return ret;
     397             :         }
     398             : 
     399             :         /* Rename request to modify distinguishedName */
     400         265 :         ret = tr_do_down_req(ac, ac->rename_req);
     401         265 :         if (ret != LDB_SUCCESS) {
     402          30 :                 ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret));
     403          30 :                 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS && ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS ) {
     404             :                         /* Windows returns Operations Error in case we can't rename the object */
     405           2 :                         return LDB_ERR_OPERATIONS_ERROR;
     406             :                 }
     407          28 :                 return ret;
     408             :         }
     409             : 
     410         235 :         return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
     411             : }
     412             : 
     413             : 
     414             : static const struct ldb_module_ops ldb_reanimate_module_ops = {
     415             :         .name           = "tombstone_reanimate",
     416             :         .modify         = tombstone_reanimate_modify,
     417             : };
     418             : 
     419        6286 : int ldb_tombstone_reanimate_module_init(const char *version)
     420             : {
     421        6286 :         LDB_MODULE_CHECK_VERSION(version);
     422        6286 :         return ldb_register_module(&ldb_reanimate_module_ops);
     423             : }

Generated by: LCOV version 1.14