s4:dsdb/samldb: add DSDB_CONTROL_PASSWORD_DEFAULT_LAST_SET_OID when defaulting pwdLas...
[metze/samba/wip.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                 uint32_t primary_group_rid;
239                 /* restoring 'user' instance attribute is heavily borrowed from samldb.c */
240
241                 /* Default values */
242                 ret = dsdb_user_obj_set_defaults(ldb, new_msg, NULL);
243                 if (ret != LDB_SUCCESS) return ret;
244
245                 /* Following are set only while reanimating objects */
246                 ret = samdb_find_or_add_attribute(ldb, new_msg,
247                                                   "adminCount", "0");
248                 if (ret != LDB_SUCCESS) return ret;
249                 ret = samdb_find_or_add_attribute(ldb, new_msg,
250                                                   "operatorCount", "0");
251                 if (ret != LDB_SUCCESS) return ret;
252
253                 /* "userAccountControl" must exists on deleted object */
254                 user_account_control = ldb_msg_find_attr_as_uint(cur_msg, "userAccountControl", (uint32_t)-1);
255                 if (user_account_control == (uint32_t)-1) {
256                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
257                                          "reanimate: No 'userAccountControl' attribute found!");
258                 }
259
260                 /* restore "sAMAccountType" */
261                 ret = dsdb_user_obj_set_account_type(ldb, new_msg, user_account_control, NULL);
262                 if (ret != LDB_SUCCESS) {
263                         return ret;
264                 }
265
266                 /* "userAccountControl" -> "primaryGroupID" mapping */
267                 ret = dsdb_user_obj_set_primary_group_id(ldb, new_msg, user_account_control, &primary_group_rid);
268                 if (ret != LDB_SUCCESS) {
269                         return ret;
270                 }
271                 /*
272                  * Older AD deployments don't know about the
273                  * RODC group
274                  */
275                 if (primary_group_rid == DOMAIN_RID_READONLY_DCS) {
276                         /* TODO:  check group exists */
277                 }
278
279         }
280
281         /* objectClass is GROUP */
282         if (samdb_find_attribute(ldb, cur_msg, "objectclass", "group") != NULL) {
283                 /* "groupType" -> "sAMAccountType" */
284                 uint32_t group_type;
285
286                 el = ldb_msg_find_element(cur_msg, "groupType");
287                 if (el == NULL) {
288                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
289                                          "reanimate: Unexpected: missing groupType attribute.");
290                 }
291
292                 group_type = ldb_msg_find_attr_as_uint(cur_msg,
293                                                        "groupType", 0);
294
295                 account_type = ds_gtype2atype(group_type);
296                 if (account_type == 0) {
297                         return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
298                                          "reanimate: Unrecognized account type!");
299                 }
300                 ret = samdb_msg_add_uint(ldb, new_msg, new_msg,
301                                          "sAMAccountType", account_type);
302                 if (ret != LDB_SUCCESS) {
303                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
304                                          "reanimate: Failed to add sAMAccountType to restored object.");
305                 }
306                 el = ldb_msg_find_element(new_msg, "sAMAccountType");
307                 el->flags = LDB_FLAG_MOD_REPLACE;
308
309                 /* Default values set by Windows */
310                 ret = samdb_find_or_add_attribute(ldb, new_msg,
311                                                   "adminCount", "0");
312                 if (ret != LDB_SUCCESS) return ret;
313                 ret = samdb_find_or_add_attribute(ldb, new_msg,
314                                                   "operatorCount", "0");
315                 if (ret != LDB_SUCCESS) return ret;
316         }
317
318         return LDB_SUCCESS;
319 }
320
321 /**
322  * Handle special LDAP modify request to restore deleted objects
323  */
324 static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_request *req)
325 {
326         int                             ret;
327         struct ldb_context              *ldb;
328         struct ldb_dn                   *dn_new;
329         struct ldb_dn                   *objectcategory;
330         struct ldb_message_element      *el_dn = NULL;
331         struct ldb_message              *msg;
332         struct ldb_result               *res_obj;
333         struct tr_context               *ac;
334
335         ldb = ldb_module_get_ctx(module);
336
337         ldb_debug(ldb, LDB_DEBUG_TRACE, "%s\n", __PRETTY_FUNCTION__);
338
339         /* do not manipulate our control entries */
340         if (ldb_dn_is_special(req->op.mod.message->dn)) {
341                 return ldb_next_request(module, req);
342         }
343
344         /* Check if this is a reanimate request */
345         if (!is_tombstone_reanimate_request(req, &el_dn)) {
346                 return ldb_next_request(module, req);
347         }
348
349         ac = tr_init_context(module, req);
350         if (ac == NULL) {
351                 return ldb_operr(ldb);
352         }
353
354         /* Load original object */
355         ret = dsdb_module_search_dn(module, req, &res_obj, req->op.mod.message->dn, NULL, DSDB_FLAG_TOP_MODULE | DSDB_SEARCH_SHOW_DELETED, req);
356         if (ret != LDB_SUCCESS) {
357                 return ldb_operr(ldb);
358         }
359         /* check if it a Deleted Object */
360         if (!ldb_msg_find_attr_as_bool(res_obj->msgs[0], "isDeleted", false)) {
361                 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, "Trying to restore not deleted object\n");
362         }
363
364         /* Simple implementation */
365
366         /* Modify request to: */
367         msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
368         if (msg == NULL) {
369                 return ldb_module_oom(ac->module);
370         }
371         /* - remove distinguishedName - we don't need it */
372         ldb_msg_remove_attr(msg, "distinguishedName");
373
374         /* restore attributed depending on objectClass */
375         ret = tr_restore_attributes(ldb, res_obj->msgs[0], msg);
376         if (ret != LDB_SUCCESS) {
377                 return ret;
378         }
379
380         /* - restore objectCategory if not present */
381         objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, msg,
382                                                  "objectCategory");
383         if (objectcategory == NULL) {
384                 const char *value;
385
386                 ret = dsdb_make_object_category(ldb, ac->schema, res_obj->msgs[0], msg, &value);
387                 if (ret != LDB_SUCCESS) {
388                         return ret;
389                 }
390
391                 ret = ldb_msg_add_string(msg, "objectCategory", value);
392                 if (ret != LDB_SUCCESS) {
393                         return ret;
394                 }
395                 msg->elements[msg->num_elements-1].flags = LDB_FLAG_MOD_ADD;
396         }
397         ret = tr_do_modify(module, req, msg);
398         if (ret != LDB_SUCCESS) {
399                 return ret;
400         }
401
402         /* Rename request to modify distinguishedName */
403         dn_new = ldb_dn_from_ldb_val(req, ldb, &el_dn->values[0]);
404         if (dn_new == NULL) {
405                 return ldb_oom(ldb);
406         }
407         ret = tr_do_rename(module, req, req->op.mod.message->dn, dn_new);
408         if (ret != LDB_SUCCESS) {
409                 ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret));
410                 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS && ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS ) {
411                         /* Windows returns Operations Error in case we can't rename the object */
412                         return LDB_ERR_OPERATIONS_ERROR;
413                 }
414                 return ret;
415         }
416
417         return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
418 }
419
420
421 static const struct ldb_module_ops ldb_reanimate_module_ops = {
422         .name           = "tombstone_reanimate",
423         .modify         = tombstone_reanimate_modify,
424 };
425
426 int ldb_tombstone_reanimate_module_init(const char *version)
427 {
428         LDB_MODULE_CHECK_VERSION(version);
429         /*
430          * Skip module registration for now.
431          * In order to enable the module again, it should be
432          * included in samba_dsdb.c between "objectclass" and
433          * "descriptor" modules.
434         return ldb_register_module(&ldb_reanimate_module_ops);
435         */
436         DEBUG(5,("Module 'tombstone_reanimate' is disabled. Skip registration."));
437         return LDB_SUCCESS;
438 }