dsdb: audit samdb and password changes
[sfrench/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         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 static struct tr_context *tr_init_context(struct ldb_module *module,
76                                           struct ldb_request *req)
77 {
78         struct ldb_context *ldb = ldb_module_get_ctx(module);
79         struct tr_context *ac;
80
81         ac = talloc_zero(req, struct tr_context);
82         if (ac == NULL) {
83                 ldb_oom(ldb);
84                 return NULL;
85         }
86
87         ac->module = module;
88         ac->req = req;
89         ac->req_msg = req->op.mod.message;
90         ac->schema = dsdb_get_schema(ldb, ac);
91
92         return ac;
93 }
94
95
96 static bool is_tombstone_reanimate_request(struct ldb_request *req,
97                                            const struct ldb_message_element **pel_dn)
98 {
99         struct ldb_message_element *el_dn;
100         struct ldb_message_element *el_deleted;
101
102         /* check distinguishedName requirement */
103         el_dn = ldb_msg_find_element(req->op.mod.message, "distinguishedName");
104         if (el_dn == NULL) {
105                 return false;
106         }
107         if (el_dn->flags != LDB_FLAG_MOD_REPLACE) {
108                 return false;
109         }
110         if (el_dn->num_values != 1) {
111                 return false;
112         }
113
114         /* check isDeleted requirement */
115         el_deleted = ldb_msg_find_element(req->op.mod.message, "isDeleted");
116         if (el_deleted == NULL) {
117                 return false;
118         }
119
120         if (el_deleted->flags != LDB_FLAG_MOD_DELETE) {
121                 return false;
122         }
123
124         *pel_dn = el_dn;
125         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 static int tr_prepare_rename(struct tr_context *ac,
133                              const struct ldb_message_element *new_dn)
134 {
135         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
136         int ret;
137
138         ac->rename_dn = ldb_dn_from_ldb_val(ac, ldb, &new_dn->values[0]);
139         if (ac->rename_dn == NULL) {
140                 return ldb_module_oom(ac->module);
141         }
142
143         ac->rename_res = talloc_zero(ac, struct ldb_result);
144         if (ac->rename_res == NULL) {
145                 return ldb_module_oom(ac->module);
146         }
147
148         ret = ldb_build_rename_req(&ac->rename_req, ldb, ac,
149                                    ac->req_msg->dn,
150                                    ac->rename_dn,
151                                    NULL,
152                                    ac->rename_res,
153                                    ldb_modify_default_callback,
154                                    ac->req);
155         LDB_REQ_SET_LOCATION(ac->rename_req);
156         if (ret != LDB_SUCCESS) {
157                 return ret;
158         }
159
160         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 static int tr_do_down_req(struct tr_context *ac, struct ldb_request *down_req)
168 {
169         int ret;
170
171         /* We need this since object is 'delete' atm */
172         ret = ldb_request_add_control(down_req,
173                                       LDB_CONTROL_SHOW_DELETED_OID,
174                                       false, NULL);
175         if (ret != LDB_SUCCESS) {
176                 return ret;
177         }
178
179         /* mark request as part of Tombstone reanimation */
180         ret = ldb_request_add_control(down_req,
181                                       DSDB_CONTROL_RESTORE_TOMBSTONE_OID,
182                                       false, NULL);
183         if (ret != LDB_SUCCESS) {
184                 return ret;
185         }
186
187         /* Run request from Next module */
188         ret = ldb_next_request(ac->module, down_req);
189         if (ret == LDB_SUCCESS) {
190                 ret = ldb_wait(down_req->handle, LDB_WAIT_ALL);
191         }
192
193         return ret;
194 }
195
196 static int tr_prepare_attributes(struct tr_context *ac)
197 {
198         struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
199         int ret;
200         struct ldb_message_element *el = NULL;
201         uint32_t account_type, user_account_control;
202         struct ldb_dn *objectcategory = NULL;
203
204         ac->mod_msg = ldb_msg_copy_shallow(ac, ac->req_msg);
205         if (ac->mod_msg == NULL) {
206                 return ldb_oom(ldb);
207         }
208
209         ac->mod_res = talloc_zero(ac, struct ldb_result);
210         if (ac->mod_res == NULL) {
211                 return ldb_oom(ldb);
212         }
213
214         ret = ldb_build_mod_req(&ac->mod_req, ldb, ac,
215                                 ac->mod_msg,
216                                 NULL,
217                                 ac->mod_res,
218                                 ldb_modify_default_callback,
219                                 ac->req);
220         LDB_REQ_SET_LOCATION(ac->mod_req);
221         if (ret != LDB_SUCCESS) {
222                 return ret;
223         }
224
225         /* - remove distinguishedName - we don't need it */
226         ldb_msg_remove_attr(ac->mod_msg, "distinguishedName");
227
228         /* remove isRecycled */
229         ret = ldb_msg_add_empty(ac->mod_msg, "isRecycled",
230                                 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, ac->search_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, ac->mod_msg, ac->mod_req);
243                 if (ret != LDB_SUCCESS) return ret;
244
245                 /* "userAccountControl" must exists on deleted object */
246                 user_account_control = ldb_msg_find_attr_as_uint(ac->search_msg,
247                                                         "userAccountControl",
248                                                         (uint32_t)-1);
249                 if (user_account_control == (uint32_t)-1) {
250                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
251                                          "reanimate: No 'userAccountControl' attribute found!");
252                 }
253
254                 /* restore "sAMAccountType" */
255                 ret = dsdb_user_obj_set_account_type(ldb, ac->mod_msg,
256                                                      user_account_control, NULL);
257                 if (ret != LDB_SUCCESS) {
258                         return ret;
259                 }
260
261                 /* "userAccountControl" -> "primaryGroupID" mapping */
262                 ret = dsdb_user_obj_set_primary_group_id(ldb, ac->mod_msg,
263                                                          user_account_control,
264                                                          &primary_group_rid);
265                 if (ret != LDB_SUCCESS) {
266                         return ret;
267                 }
268                 /*
269                  * Older AD deployments don't know about the
270                  * RODC group
271                  */
272                 if (primary_group_rid == DOMAIN_RID_READONLY_DCS) {
273                         /* TODO:  check group exists */
274                 }
275
276         }
277
278         /* objectClass is GROUP */
279         if (samdb_find_attribute(ldb, ac->search_msg, "objectclass", "group") != NULL) {
280                 /* "groupType" -> "sAMAccountType" */
281                 uint32_t group_type;
282
283                 el = ldb_msg_find_element(ac->search_msg, "groupType");
284                 if (el == NULL) {
285                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
286                                          "reanimate: Unexpected: missing groupType attribute.");
287                 }
288
289                 group_type = ldb_msg_find_attr_as_uint(ac->search_msg,
290                                                        "groupType", 0);
291
292                 account_type = ds_gtype2atype(group_type);
293                 if (account_type == 0) {
294                         return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
295                                          "reanimate: Unrecognized account type!");
296                 }
297                 ret = samdb_msg_add_uint(ldb, ac->mod_msg, ac->mod_msg,
298                                          "sAMAccountType", account_type);
299                 if (ret != LDB_SUCCESS) {
300                         return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
301                                          "reanimate: Failed to add sAMAccountType to restored object.");
302                 }
303                 el = ldb_msg_find_element(ac->mod_msg, "sAMAccountType");
304                 el->flags = LDB_FLAG_MOD_REPLACE;
305
306                 /* Default values set by Windows */
307                 ret = samdb_find_or_add_attribute(ldb, ac->mod_msg,
308                                                   "adminCount", "0");
309                 if (ret != LDB_SUCCESS) return ret;
310                 ret = samdb_find_or_add_attribute(ldb, ac->mod_msg,
311                                                   "operatorCount", "0");
312                 if (ret != LDB_SUCCESS) return ret;
313         }
314
315         /* - restore objectCategory if not present */
316         objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, ac->search_msg,
317                                                  "objectCategory");
318         if (objectcategory == NULL) {
319                 const char *value;
320
321                 ret = dsdb_make_object_category(ldb, ac->schema, ac->search_msg,
322                                                 ac->mod_msg, &value);
323                 if (ret != LDB_SUCCESS) {
324                         return ret;
325                 }
326
327                 ret = ldb_msg_add_string(ac->mod_msg, "objectCategory", value);
328                 if (ret != LDB_SUCCESS) {
329                         return ret;
330                 }
331                 el = ldb_msg_find_element(ac->mod_msg, "objectCategory");
332                 el->flags = LDB_FLAG_MOD_ADD;
333         }
334
335         return LDB_SUCCESS;
336 }
337
338 /**
339  * Handle special LDAP modify request to restore deleted objects
340  */
341 static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_request *req)
342 {
343         struct ldb_context *ldb = ldb_module_get_ctx(module);
344         const struct ldb_message_element *el_dn = NULL;
345         struct tr_context *ac = NULL;
346         int ret;
347
348         ldb_debug(ldb, LDB_DEBUG_TRACE, "%s\n", __PRETTY_FUNCTION__);
349
350         /* do not manipulate our control entries */
351         if (ldb_dn_is_special(req->op.mod.message->dn)) {
352                 return ldb_next_request(module, req);
353         }
354
355         /* Check if this is a reanimate request */
356         if (!is_tombstone_reanimate_request(req, &el_dn)) {
357                 return ldb_next_request(module, req);
358         }
359
360         ac = tr_init_context(module, req);
361         if (ac == NULL) {
362                 return ldb_operr(ldb);
363         }
364
365         /* Load original object */
366         ret = dsdb_module_search_dn(module, ac, &ac->search_res,
367                                     ac->req_msg->dn, NULL,
368                                     DSDB_FLAG_TOP_MODULE |
369                                     DSDB_SEARCH_SHOW_DELETED,
370                                     req);
371         if (ret != LDB_SUCCESS) {
372                 return ldb_operr(ldb);
373         }
374         ac->search_msg = ac->search_res->msgs[0];
375
376         /* check if it a Deleted Object */
377         if (!ldb_msg_find_attr_as_bool(ac->search_msg, "isDeleted", false)) {
378                 return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, "Trying to restore not deleted object\n");
379         }
380
381         /* Simple implementation */
382
383         /* prepare attributed depending on objectClass */
384         ret = tr_prepare_attributes(ac);
385         if (ret != LDB_SUCCESS) {
386                 return ret;
387         }
388
389         /* Rename request to modify distinguishedName */
390         ret = tr_prepare_rename(ac, el_dn);
391         if (ret != LDB_SUCCESS) {
392                 return ret;
393         }
394
395         /* restore attributed depending on objectClass */
396         ret = tr_do_down_req(ac, ac->mod_req);
397         if (ret != LDB_SUCCESS) {
398                 return ret;
399         }
400
401         /* Rename request to modify distinguishedName */
402         ret = tr_do_down_req(ac, ac->rename_req);
403         if (ret != LDB_SUCCESS) {
404                 ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret));
405                 if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS && ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS ) {
406                         /* Windows returns Operations Error in case we can't rename the object */
407                         return LDB_ERR_OPERATIONS_ERROR;
408                 }
409                 return ret;
410         }
411
412         return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
413 }
414
415
416 static const struct ldb_module_ops ldb_reanimate_module_ops = {
417         .name           = "tombstone_reanimate",
418         .modify         = tombstone_reanimate_modify,
419 };
420
421 int ldb_tombstone_reanimate_module_init(const char *version)
422 {
423         LDB_MODULE_CHECK_VERSION(version);
424         return ldb_register_module(&ldb_reanimate_module_ops);
425 }