s4-dsdb: Common helper for setting "sAMAccountType" on User objects
[kai/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / tombstone_reanimate.c
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
57         struct ldb_module *module;
58         struct ldb_request *req;
59         const struct dsdb_schema *schema;
60
61         struct ldb_reply *search_res;
62         struct ldb_reply *search_res2;
63
64         int (*step_fn)(struct tr_context *);
65 };
66
67 static struct tr_context *tr_init_context(struct ldb_module *module,
68                                           struct ldb_request *req)
69 {
70         struct ldb_context *ldb;
71         struct tr_context *ac;
72
73         ldb = ldb_module_get_ctx(module);
74
75         ac = talloc_zero(req, struct tr_context);
76         if (ac == NULL) {
77                 ldb_oom(ldb);
78                 return NULL;
79         }
80
81         ac->module = module;
82         ac->req = req;
83         ac->schema = dsdb_get_schema(ldb, ac);
84
85         return ac;
86 }
87
88
89 static bool is_tombstone_reanimate_request(struct ldb_request *req, struct ldb_message_element **pel_dn)
90 {
91         struct ldb_message_element *el_dn;
92         struct ldb_message_element *el_deleted;
93
94         /* check distinguishedName requirement */
95         el_dn = ldb_msg_find_element(req->op.mod.message, "distinguishedName");
96         if (el_dn == NULL || el_dn->flags != LDB_FLAG_MOD_REPLACE) {
97                 return false;
98         }
99
100         /* check isDeleted requirement */
101         el_deleted = ldb_msg_find_element(req->op.mod.message, "isDeleted");
102         if (el_deleted == NULL || el_deleted->flags != LDB_FLAG_MOD_DELETE) {
103                 return false;
104         }
105
106         *pel_dn = el_dn;
107         return true;
108 }
109
110 /**
111  * Local rename implementation based on dsdb_module_rename()
112  * so we could fine tune it and add more controls
113  */
114 static int _tr_do_rename(struct ldb_module *module, struct ldb_request *parent_req,
115                          struct ldb_dn *dn_from, struct ldb_dn *dn_to)
116 {
117         int                     ret;
118         struct ldb_request      *req;
119         struct ldb_context      *ldb = ldb_module_get_ctx(module);
120         TALLOC_CTX              *tmp_ctx = talloc_new(parent_req);
121         struct ldb_result       *res;
122
123         res = talloc_zero(tmp_ctx, struct ldb_result);
124         if (!res) {
125                 talloc_free(tmp_ctx);
126                 return ldb_oom(ldb_module_get_ctx(module));
127         }
128
129         ret = ldb_build_rename_req(&req, ldb, tmp_ctx,
130                                    dn_from,
131                                    dn_to,
132                                    NULL,
133                                    res,
134                                    ldb_modify_default_callback,
135                                    parent_req);
136         LDB_REQ_SET_LOCATION(req);
137         if (ret != LDB_SUCCESS) {
138                 talloc_free(tmp_ctx);
139                 return ret;
140         }
141
142         ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL);
143         if (ret != LDB_SUCCESS) {
144                 talloc_free(tmp_ctx);
145                 return ret;
146         }
147
148         /* mark request as part of Tombstone reanimation */
149         ret = ldb_request_add_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID, false, NULL);
150         if (ret != LDB_SUCCESS) {
151                 talloc_free(tmp_ctx);
152                 return ret;
153         }
154
155         /*
156          * Run request from the top module
157          * so we get show_deleted control OID resolved
158          */
159         ret = ldb_next_request(module, req);
160         if (ret == LDB_SUCCESS) {
161                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
162         }
163
164         talloc_free(tmp_ctx);
165         return ret;
166 }
167
168 /**
169  * Local rename implementation based on dsdb_module_modify()
170  * so we could fine tune it and add more controls
171  */
172 static int _tr_do_modify(struct ldb_module *module, struct ldb_request *parent_req, struct ldb_message *msg)
173 {
174         int                     ret;
175         struct ldb_request      *mod_req;
176         struct ldb_context      *ldb = ldb_module_get_ctx(module);
177         TALLOC_CTX              *tmp_ctx = talloc_new(parent_req);
178         struct ldb_result       *res;
179
180         res = talloc_zero(tmp_ctx, struct ldb_result);
181         if (!res) {
182                 talloc_free(tmp_ctx);
183                 return ldb_oom(ldb_module_get_ctx(module));
184         }
185
186         ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
187                                 msg,
188                                 NULL,
189                                 res,
190                                 ldb_modify_default_callback,
191                                 parent_req);
192         LDB_REQ_SET_LOCATION(mod_req);
193         if (ret != LDB_SUCCESS) {
194                 talloc_free(tmp_ctx);
195                 return ret;
196         }
197
198         /* We need this since object is 'delete' atm */
199         ret = ldb_request_add_control(mod_req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL);
200         if (ret != LDB_SUCCESS) {
201                 talloc_free(tmp_ctx);
202                 return ret;
203         }
204
205         /* mark request as part of Tombstone reanimation */
206         ret = ldb_request_add_control(mod_req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID, false, NULL);
207         if (ret != LDB_SUCCESS) {
208                 talloc_free(tmp_ctx);
209                 return ret;
210         }
211
212         /* Run request from Next module */
213         ret = ldb_next_request(module, mod_req);
214         if (ret == LDB_SUCCESS) {
215                 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
216         }
217
218         talloc_free(tmp_ctx);
219         return ret;
220 }
221
222 static int _tr_restore_attributes(struct ldb_context *ldb, struct ldb_message *cur_msg, struct ldb_message *new_msg)
223 {
224         int                             ret;
225         struct ldb_message_element      *el;
226         uint32_t                        account_type, user_account_control;
227
228
229         /* remove isRecycled */
230         ret = ldb_msg_add_empty(new_msg, "isRecycled", LDB_FLAG_MOD_DELETE, NULL);
231         if (ret != LDB_SUCCESS) {
232                 ldb_asprintf_errstring(ldb, "Failed to reset isRecycled attribute: %s", ldb_strerror(ret));
233                 return LDB_ERR_OPERATIONS_ERROR;
234         }
235
236         /* objectClass is USER */
237         if (samdb_find_attribute(ldb, cur_msg, "objectclass", "user") != NULL) {
238                 /* restoring 'user' instance attribute is heavily borrowed from samldb.c */
239
240                 /* Default values */
241                 ret = dsdb_user_obj_set_defaults(ldb, new_msg);
242                 if (ret != LDB_SUCCESS) return ret;
243
244                 /* Following are set only while reanimating objects */
245                 ret = samdb_find_or_add_attribute(ldb, new_msg,
246                                                   "adminCount", "0");
247                 if (ret != LDB_SUCCESS) return ret;
248                 ret = samdb_find_or_add_attribute(ldb, new_msg,
249                                                   "operatorCount", "0");
250                 if (ret != LDB_SUCCESS) return ret;
251
252                 /* "userAccountControl" must exists on deleted object */
253                 user_account_control = ldb_msg_find_attr_as_uint(cur_msg, "userAccountControl", (uint32_t)-1);
254                 if (user_account_control == (uint32_t)-1) {
255                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
256                                          "reanimate: No 'userAccountControl' attribute found!");
257                 }
258
259                 /* restore "sAMAccountType" */
260                 ret = dsdb_user_obj_set_account_type(ldb, new_msg, user_account_control, NULL);
261                 if (ret != LDB_SUCCESS) {
262                         return ret;
263                 }
264
265                 /* "userAccountControl" -> "primaryGroupID" mapping */
266                 if (!ldb_msg_find_element(new_msg, "primaryGroupID")) {
267                         uint32_t rid = ds_uf2prim_group_rid(user_account_control);
268
269                         ret = samdb_msg_add_uint(ldb, new_msg, new_msg,
270                                                  "primaryGroupID", rid);
271                         if (ret != LDB_SUCCESS) {
272                                 return ret;
273                         }
274                         el = ldb_msg_find_element(new_msg, "primaryGroupID");
275                         el->flags = LDB_FLAG_MOD_REPLACE;
276                 }
277         }
278
279         /* objectClass is GROUP */
280         if (samdb_find_attribute(ldb, cur_msg, "objectclass", "group") != NULL) {
281                 /* "groupType" -> "sAMAccountType" */
282                 uint32_t group_type;
283
284                 el = ldb_msg_find_element(cur_msg, "groupType");
285                 if (el == NULL) {
286                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
287                                          "reanimate: Unexpected: missing groupType attribute.");
288                 }
289
290                 group_type = ldb_msg_find_attr_as_uint(cur_msg,
291                                                        "groupType", 0);
292
293                 account_type = ds_gtype2atype(group_type);
294                 if (account_type == 0) {
295                         return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
296                                          "reanimate: Unrecognized account type!");
297                 }
298                 ret = samdb_msg_add_uint(ldb, new_msg, new_msg,
299                                          "sAMAccountType", account_type);
300                 if (ret != LDB_SUCCESS) {
301                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
302                                          "reanimate: Failed to add sAMAccountType to restored object.");
303                 }
304                 el = ldb_msg_find_element(new_msg, "sAMAccountType");
305                 el->flags = LDB_FLAG_MOD_REPLACE;
306
307                 /* Default values set by Windows */
308                 ret = samdb_find_or_add_attribute(ldb, new_msg,
309                                                   "adminCount", "0");
310                 if (ret != LDB_SUCCESS) return ret;
311                 ret = samdb_find_or_add_attribute(ldb, new_msg,
312                                                   "operatorCount", "0");
313                 if (ret != LDB_SUCCESS) return ret;
314         }
315
316         return LDB_SUCCESS;
317 }
318
319 /**
320  * Handle special LDAP modify request to restore deleted objects
321  */
322 static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_request *req)
323 {
324         int                             ret;
325         struct ldb_context              *ldb;
326         struct ldb_dn                   *dn_new;
327         struct ldb_dn                   *objectcategory;
328         struct ldb_message_element      *el_dn = NULL;
329         struct ldb_message              *msg;
330         struct ldb_result               *res_obj;
331         struct tr_context               *ac;
332
333         ldb = ldb_module_get_ctx(module);
334
335         ldb_debug(ldb, LDB_DEBUG_TRACE, "%s\n", __PRETTY_FUNCTION__);
336
337         /* do not manipulate our control entries */
338         if (ldb_dn_is_special(req->op.mod.message->dn)) {
339                 return ldb_next_request(module, req);
340         }
341
342         /* Check if this is a reanimate request */
343         if (!is_tombstone_reanimate_request(req, &el_dn)) {
344                 return ldb_next_request(module, req);
345         }
346
347         ac = tr_init_context(module, req);
348         if (ac == NULL) {
349                 return ldb_operr(ldb);
350         }
351
352         /* Load original object */
353         ret = dsdb_module_search_dn(module, req, &res_obj, req->op.mod.message->dn, NULL, DSDB_FLAG_TOP_MODULE | DSDB_SEARCH_SHOW_DELETED, req);
354         if (ret != LDB_SUCCESS) {
355                 return ldb_operr(ldb);
356         }
357         /* check if it a Deleted Object */
358         if (!ldb_msg_find_attr_as_bool(res_obj->msgs[0], "isDeleted", false)) {
359                 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, "Trying to restore not deleted object\n");
360         }
361
362         /* Simple implementation */
363
364         /* Modify request to: */
365         msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
366         if (msg == NULL) {
367                 return ldb_module_oom(ac->module);
368         }
369         /* - remove distinguishedName - we don't need it */
370         ldb_msg_remove_attr(msg, "distinguishedName");
371
372         /* restore attributed depending on objectClass */
373         ret = _tr_restore_attributes(ldb, res_obj->msgs[0], msg);
374         if (ret != LDB_SUCCESS) {
375                 return ret;
376         }
377
378         /* - restore objectCategory if not present */
379         objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, msg,
380                                                  "objectCategory");
381         if (objectcategory == NULL) {
382                 const char *value;
383
384                 ret = dsdb_make_object_category(ldb, ac->schema, res_obj->msgs[0], msg, &value);
385                 if (ret != LDB_SUCCESS) {
386                         return ret;
387                 }
388
389                 ret = ldb_msg_add_string(msg, "objectCategory", value);
390                 if (ret != LDB_SUCCESS) {
391                         return ret;
392                 }
393                 msg->elements[msg->num_elements-1].flags = LDB_FLAG_MOD_ADD;
394         }
395         ret = _tr_do_modify(module, req, msg);
396         if (ret != LDB_SUCCESS) {
397                 return ret;
398         }
399
400         /* Rename request to modify distinguishedName */
401         dn_new = ldb_dn_from_ldb_val(req, ldb, &el_dn->values[0]);
402         if (dn_new == NULL) {
403                 return ldb_oom(ldb);
404         }
405         ret = _tr_do_rename(module, req, req->op.mod.message->dn, dn_new);
406         if (ret != LDB_SUCCESS) {
407                 ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret));
408                 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS && ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS ) {
409                         /* Windows returns Operations Error in case we can't rename the object */
410                         return LDB_ERR_OPERATIONS_ERROR;
411                 }
412                 return ret;
413         }
414
415         return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
416 }
417
418
419 static const struct ldb_module_ops ldb_reanimate_module_ops = {
420         .name           = "tombstone_reanimate",
421         .modify         = tombstone_reanimate_modify,
422 };
423
424 int ldb_tombstone_reanimate_module_init(const char *version)
425 {
426         LDB_MODULE_CHECK_VERSION(version);
427         return ldb_register_module(&ldb_reanimate_module_ops);
428 }