Fixed ACL module to use dsdb_module_* API.
[kamenim/samba.git] / source4 / dsdb / samdb / ldb_modules / acl.c
1 /*
2   ldb database library
3
4   Copyright (C) Simo Sorce 2006-2008
5   Copyright (C) Nadezhda Ivanova 2009
6   Copyright (C) Anatoliy Atanasov  2009
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /*
23  *  Name: ldb
24  *
25  *  Component: ldb ACL module
26  *
27  *  Description: Module that performs authorisation access checks based on the
28  *               account's security context and the DACL of the object being polled.
29  *               Only DACL checks implemented at this point
30  *
31  *  Authors: Nadezhda Ivanova, Anatoliy Atanasov
32  */
33
34 #include "includes.h"
35 #include "ldb_module.h"
36 #include "auth/auth.h"
37 #include "libcli/security/security.h"
38 #include "dsdb/samdb/samdb.h"
39 #include "librpc/gen_ndr/ndr_security.h"
40 #include "param/param.h"
41 #include "dsdb/samdb/ldb_modules/util.h"
42 #include "lib/util/tsort.h"
43
44 struct extended_access_check_attribute {
45         const char *oa_name;
46         const uint32_t requires_rights;
47 };
48
49 struct acl_private {
50         bool acl_perform;
51         const char **password_attrs;
52 };
53
54 struct acl_context {
55         struct ldb_module *module;
56         struct ldb_request *req;
57         bool am_system;
58         bool allowedAttributes;
59         bool allowedAttributesEffective;
60         bool allowedChildClasses;
61         bool allowedChildClassesEffective;
62         bool sDRightsEffective;
63         const char * const *attrs;
64 };
65
66 bool is_root_base_dn(struct ldb_context *ldb, struct ldb_dn *dn_to_check)
67 {
68         int result;
69         struct ldb_dn *root_base_dn = ldb_get_root_basedn(ldb);
70         result = ldb_dn_compare(root_base_dn,dn_to_check);
71         return (result==0);
72 }
73
74 static struct security_token *acl_user_token(struct ldb_module *module)
75 {
76         struct ldb_context *ldb = ldb_module_get_ctx(module);
77         struct auth_session_info *session_info
78                 = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
79         if(!session_info) {
80                 return NULL;
81         }
82         return session_info->security_token;
83 }
84
85 static int acl_module_init(struct ldb_module *module)
86 {
87         struct ldb_context *ldb;
88         struct acl_private *data;
89         int ret, i;
90         TALLOC_CTX *mem_ctx = talloc_new(module);
91         static const char *attrs[] = { "passwordAttribute", NULL };
92         struct ldb_result *res;
93         struct ldb_message *msg;
94         struct ldb_message_element *password_attributes;
95
96         ldb = ldb_module_get_ctx(module);
97
98         ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
99         if (ret != LDB_SUCCESS) {
100                 ldb_debug(ldb, LDB_DEBUG_ERROR,
101                           "acl_module_init: Unable to register control with rootdse!\n");
102                 return LDB_ERR_OPERATIONS_ERROR;
103         }
104
105         data = talloc(module, struct acl_private);
106         if (data == NULL) {
107                 ldb_oom(ldb);
108                 return LDB_ERR_OPERATIONS_ERROR;
109         }
110
111         data->password_attrs = NULL;
112         data->acl_perform = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"),
113                                          NULL, "acl", "perform", false);
114         ldb_module_set_private(module, data);
115
116         if (!mem_ctx) {
117                 ldb_oom(ldb);
118                 return LDB_ERR_OPERATIONS_ERROR;
119         }
120
121         ret = dsdb_module_search_dn(module, mem_ctx, &res,
122                                     ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"),
123                                     attrs, 0);
124         if (ret != LDB_SUCCESS) {
125                 goto done;
126         }
127         if (res->count == 0) {
128                 goto done;
129         }
130
131         if (res->count > 1) {
132                 talloc_free(mem_ctx);
133                 return LDB_ERR_CONSTRAINT_VIOLATION;
134         }
135
136         msg = res->msgs[0];
137
138         password_attributes = ldb_msg_find_element(msg, "passwordAttribute");
139         if (!password_attributes) {
140                 goto done;
141         }
142         data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1);
143         if (!data->password_attrs) {
144                 talloc_free(mem_ctx);
145                 ldb_oom(ldb);
146                 return LDB_ERR_OPERATIONS_ERROR;
147         }
148         for (i=0; i < password_attributes->num_values; i++) {
149                 data->password_attrs[i] = (const char *)password_attributes->values[i].data;
150                 talloc_steal(data->password_attrs, password_attributes->values[i].data);
151         }
152         data->password_attrs[i] = NULL;
153
154 done:
155         talloc_free(mem_ctx);
156         return ldb_next_init(module);
157 }
158
159 static const struct GUID *get_oc_guid_from_message(struct ldb_module *module,
160                                                    struct ldb_message *msg)
161 {
162         struct ldb_message_element *oc_el;
163         struct ldb_context *ldb = ldb_module_get_ctx(module);
164
165         oc_el = ldb_msg_find_element(msg, "objectClass");
166         if (!oc_el) {
167                 return NULL;
168         }
169
170         return class_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb),
171                                                       (char *)oc_el->values[oc_el->num_values-1].data);
172 }
173
174 static int acl_check_access_on_attribute(struct ldb_module *module,
175                                          TALLOC_CTX *mem_ctx,
176                                          struct security_descriptor *sd,
177                                          struct dom_sid *rp_sid,
178                                          uint32_t access,
179                                          const struct dsdb_attribute *attr)
180 {
181         int ret;
182         NTSTATUS status;
183         uint32_t access_granted;
184         struct object_tree *root = NULL;
185         struct object_tree *new_node = NULL;
186         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
187         struct security_token *token = acl_user_token(module);
188         if (attr) {
189                 if (!GUID_all_zero(&attr->attributeSecurityGUID)) {
190                         if (!insert_in_object_tree(tmp_ctx,
191                                                    &attr->attributeSecurityGUID, access,
192                                                    &root, &new_node)) {
193                                 DEBUG(10, ("acl_search: cannot add to object tree securityGUID\n"));
194                                 goto fail;
195                         }
196
197                         if (!insert_in_object_tree(tmp_ctx,
198                                                    &attr->schemaIDGUID, access, &new_node, &new_node)) {
199                                 DEBUG(10, ("acl_search: cannot add to object tree attributeGUID\n"));
200                                 goto fail;
201                         }
202                 }
203                 else {
204                         if (!insert_in_object_tree(tmp_ctx,
205                                                    &attr->schemaIDGUID, access, &root, &new_node)) {
206                                 DEBUG(10, ("acl_search: cannot add to object tree attributeGUID\n"));
207                                 goto fail;
208                         }
209                 }
210         }
211         status = sec_access_check_ds(sd, token,
212                                      access,
213                                      &access_granted,
214                                      root,
215                                      rp_sid);
216         if (!NT_STATUS_IS_OK(status)) {
217                 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
218         }
219         else {
220                 ret = LDB_SUCCESS;
221         }
222         return ret;
223 fail:
224         return LDB_ERR_OPERATIONS_ERROR;
225 }
226
227 static int acl_check_access_on_class(struct ldb_module *module,
228                                      TALLOC_CTX *mem_ctx,
229                                      struct security_descriptor *sd,
230                                      struct dom_sid *rp_sid,
231                                      uint32_t access,
232                                      const char *class_name)
233 {
234         int ret;
235         struct ldb_context *ldb = ldb_module_get_ctx(module);
236         NTSTATUS status;
237         uint32_t access_granted;
238         struct object_tree *root = NULL;
239         struct object_tree *new_node = NULL;
240         const struct GUID *guid;
241         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
242         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
243         struct security_token *token = acl_user_token(module);
244         if (class_name) {
245                 guid = class_schemaid_guid_by_lDAPDisplayName(schema, class_name);
246                 if (!guid) {
247                         DEBUG(10, ("acl_search: cannot find class %s\n",
248                                    class_name));
249                         goto fail;
250                 }
251                 if (!insert_in_object_tree(tmp_ctx,
252                                            guid, access,
253                                            &root, &new_node)) {
254                         DEBUG(10, ("acl_search: cannot add to object tree guid\n"));
255                         goto fail;
256                 }
257         }
258         status = sec_access_check_ds(sd, token,
259                                      access,
260                                      &access_granted,
261                                      root,
262                                      rp_sid);
263         if (!NT_STATUS_IS_OK(status)) {
264                 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
265         }
266         else {
267                 ret = LDB_SUCCESS;
268         }
269         return ret;
270 fail:
271         return LDB_ERR_OPERATIONS_ERROR;
272 }
273
274 static int acl_allowedAttributes(struct ldb_module *module,
275                                  struct ldb_message *sd_msg,
276                                  struct ldb_message *msg,
277                                  struct acl_context *ac)
278 {
279         struct ldb_message_element *oc_el;
280         struct ldb_context *ldb = ldb_module_get_ctx(module);
281         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
282         TALLOC_CTX *mem_ctx;
283         const char **attr_list;
284         int i, ret;
285
286         /* If we don't have a schema yet, we can't do anything... */
287         if (schema == NULL) {
288                 return LDB_SUCCESS;
289         }
290
291         /* Must remove any existing attribute */
292         if (ac->allowedAttributes) {
293                 ldb_msg_remove_attr(msg, "allowedAttributes");
294         }
295
296         mem_ctx = talloc_new(msg);
297         if (!mem_ctx) {
298                 ldb_oom(ldb);
299                 return LDB_ERR_OPERATIONS_ERROR;
300         }
301
302         oc_el = ldb_msg_find_element(sd_msg, "objectClass");
303         attr_list = dsdb_full_attribute_list(mem_ctx, schema, oc_el, DSDB_SCHEMA_ALL);
304         if (!attr_list) {
305                 ldb_asprintf_errstring(ldb, "acl: Failed to get list of attributes");
306                 talloc_free(mem_ctx);
307                 return LDB_ERR_OPERATIONS_ERROR;
308         }
309         if (ac->allowedAttributes) {
310                 for (i=0; attr_list && attr_list[i]; i++) {
311                         ldb_msg_add_string(msg, "allowedAttributes", attr_list[i]);
312                 }
313         }
314         if (ac->allowedAttributesEffective) {
315                 struct security_descriptor *sd;
316                 struct dom_sid *sid = NULL;
317                 struct ldb_control *as_system = ldb_request_get_control(ac->req,
318                                                                         LDB_CONTROL_AS_SYSTEM_OID);
319
320                 if (as_system != NULL) {
321                         as_system->critical = 0;
322                 }
323
324                 ldb_msg_remove_attr(msg, "allowedAttributesEffective");
325                 if (ac->am_system || as_system) {
326                         for (i=0; attr_list && attr_list[i]; i++) {
327                                 ldb_msg_add_string(msg, "allowedAttributesEffective", attr_list[i]);
328                         }
329                         return LDB_SUCCESS;
330                 }
331
332                 ret = dsdb_get_sd_from_ldb_message(mem_ctx, sd_msg, &sd);
333
334                 if (ret != LDB_SUCCESS) {
335                         return ret;
336                 }
337                 ret = dsdb_get_dom_sid_from_ldb_message(mem_ctx, sd_msg, &sid);
338
339                 if (ret != LDB_SUCCESS) {
340                         return ret;
341                 }
342                 for (i=0; attr_list && attr_list[i]; i++) {
343                         const struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema,
344                                                                                         attr_list[i]);
345                         if (!attr) {
346                                 return LDB_ERR_OPERATIONS_ERROR;
347                         }
348                         /* remove constructed attributes */
349                         if (attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED
350                             || attr->systemOnly
351                             || (attr->linkID != 0 && attr->linkID % 2 != 0 )) {
352                                 continue;
353                         }
354                         ret = acl_check_access_on_attribute(module,
355                                                             msg,
356                                                             sd,
357                                                             sid,
358                                                             SEC_ADS_WRITE_PROP,
359                                                             attr);
360                         if (ret == LDB_SUCCESS) {
361                                 ldb_msg_add_string(msg, "allowedAttributesEffective", attr_list[i]);
362                         }
363                 }
364         }
365         return LDB_SUCCESS;
366 }
367
368 static int acl_childClasses(struct ldb_module *module,
369                             struct ldb_message *sd_msg,
370                             struct ldb_message *msg,
371                             const char *attrName)
372 {
373         struct ldb_message_element *oc_el;
374         struct ldb_message_element *allowedClasses;
375         struct ldb_context *ldb = ldb_module_get_ctx(module);
376         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
377         const struct dsdb_class *sclass;
378         int i, j, ret;
379
380         /* If we don't have a schema yet, we can't do anything... */
381         if (schema == NULL) {
382                 return LDB_SUCCESS;
383         }
384
385         /* Must remove any existing attribute, or else confusion reins */
386         ldb_msg_remove_attr(msg, attrName);
387         ret = ldb_msg_add_empty(msg, attrName, 0, &allowedClasses);
388         if (ret != LDB_SUCCESS) {
389                 return ret;
390         }
391
392         oc_el = ldb_msg_find_element(sd_msg, "objectClass");
393
394         for (i=0; oc_el && i < oc_el->num_values; i++) {
395                 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
396                 if (!sclass) {
397                         /* We don't know this class?  what is going on? */
398                         continue;
399                 }
400
401                 for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
402                         ldb_msg_add_string(msg, attrName, sclass->possibleInferiors[j]);
403                 }
404         }
405         if (allowedClasses->num_values > 1) {
406                 TYPESAFE_QSORT(allowedClasses->values, allowedClasses->num_values, data_blob_cmp);
407                 for (i=1 ; i < allowedClasses->num_values; i++) {
408                         struct ldb_val *val1 = &allowedClasses->values[i-1];
409                         struct ldb_val *val2 = &allowedClasses->values[i];
410                         if (data_blob_cmp(val1, val2) == 0) {
411                                 memmove(val1, val2, (allowedClasses->num_values - i) * sizeof(struct ldb_val));
412                                 allowedClasses->num_values--;
413                                 i--;
414                         }
415                 }
416         }
417
418         return LDB_SUCCESS;
419 }
420
421 static int acl_childClassesEffective(struct ldb_module *module,
422                                      struct ldb_message *sd_msg,
423                                      struct ldb_message *msg,
424                                      struct acl_context *ac)
425 {
426         struct ldb_message_element *oc_el;
427         struct ldb_message_element *allowedClasses = NULL;
428         struct ldb_context *ldb = ldb_module_get_ctx(module);
429         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
430         const struct dsdb_class *sclass;
431         struct security_descriptor *sd;
432         struct ldb_control *as_system = ldb_request_get_control(ac->req,
433                                                                 LDB_CONTROL_AS_SYSTEM_OID);
434         struct dom_sid *sid = NULL;
435         int i, j, ret;
436
437         if (as_system != NULL) {
438                 as_system->critical = 0;
439         }
440
441         if (ac->am_system || as_system) {
442                 return acl_childClasses(module, sd_msg, msg, "allowedChildClassesEffective");
443         }
444
445         /* If we don't have a schema yet, we can't do anything... */
446         if (schema == NULL) {
447                 return LDB_SUCCESS;
448         }
449
450         /* Must remove any existing attribute, or else confusion reins */
451         ldb_msg_remove_attr(msg, "allowedChildClassesEffective");
452
453         oc_el = ldb_msg_find_element(sd_msg, "objectClass");
454         ret = dsdb_get_sd_from_ldb_message(msg, sd_msg, &sd);
455         if (ret != LDB_SUCCESS) {
456                 return ret;
457         }
458         ret = dsdb_get_dom_sid_from_ldb_message(msg, sd_msg, &sid);
459
460         if (ret != LDB_SUCCESS) {
461                 return ret;
462         }
463         for (i=0; oc_el && i < oc_el->num_values; i++) {
464                 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
465                 if (!sclass) {
466                         /* We don't know this class?  what is going on? */
467                         continue;
468                 }
469
470                 for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
471                         ret = acl_check_access_on_class(module,
472                                                         msg,
473                                                         sd,
474                                                         sid,
475                                                         SEC_ADS_CREATE_CHILD,
476                                                         sclass->possibleInferiors[j]);
477                         if (ret == LDB_SUCCESS) {
478                                 ldb_msg_add_string(msg, "allowedChildClassesEffective",
479                                                    sclass->possibleInferiors[j]);
480                         }
481                 }
482         }
483         allowedClasses = ldb_msg_find_element(msg, "allowedChildClassesEffective");
484         if (!allowedClasses) {
485                 return LDB_SUCCESS;
486         }
487
488         if (allowedClasses->num_values > 1) {
489                 TYPESAFE_QSORT(allowedClasses->values, allowedClasses->num_values, data_blob_cmp);
490                 for (i=1 ; i < allowedClasses->num_values; i++) {
491                         struct ldb_val *val1 = &allowedClasses->values[i-1];
492                         struct ldb_val *val2 = &allowedClasses->values[i];
493                         if (data_blob_cmp(val1, val2) == 0) {
494                                 memmove(val1, val2, (allowedClasses->num_values - i) * sizeof( struct ldb_val));
495                                 allowedClasses->num_values--;
496                                 i--;
497                         }
498                 }
499         }
500         return LDB_SUCCESS;
501 }
502
503 static int acl_sDRightsEffective(struct ldb_module *module,
504                                  struct ldb_message *sd_msg,
505                                  struct ldb_message *msg,
506                                  struct acl_context *ac)
507 {
508         struct ldb_message_element *rightsEffective;
509         int ret;
510         struct security_descriptor *sd;
511         struct ldb_control *as_system = ldb_request_get_control(ac->req,
512                                                                 LDB_CONTROL_AS_SYSTEM_OID);
513         struct dom_sid *sid = NULL;
514         uint32_t flags = 0;
515
516         if (as_system != NULL) {
517                 as_system->critical = 0;
518         }
519
520         /* Must remove any existing attribute, or else confusion reins */
521         ldb_msg_remove_attr(msg, "sDRightsEffective");
522         ret = ldb_msg_add_empty(msg, "sDRightsEffective", 0, &rightsEffective);
523         if (ret != LDB_SUCCESS) {
524                 return ret;
525         }
526         if (ac->am_system || as_system) {
527                 flags = SECINFO_OWNER | SECINFO_GROUP |  SECINFO_SACL |  SECINFO_DACL;
528         }
529         else {
530                 /* Get the security descriptor from the message */
531                 ret = dsdb_get_sd_from_ldb_message(msg, sd_msg, &sd);
532                 if (ret != LDB_SUCCESS) {
533                         return ret;
534                 }
535                 ret = dsdb_get_dom_sid_from_ldb_message(msg, sd_msg, &sid);
536
537                 if (ret != LDB_SUCCESS) {
538                         return ret;
539                 }
540                 ret = acl_check_access_on_attribute(module,
541                                                     msg,
542                                                     sd,
543                                                     sid,
544                                                     SEC_STD_WRITE_OWNER,
545                                                     NULL);
546                 if (ret == LDB_SUCCESS) {
547                         flags |= SECINFO_OWNER | SECINFO_GROUP;
548                 }
549                 ret = acl_check_access_on_attribute(module,
550                                                     msg,
551                                                     sd,
552                                                     sid,
553                                                     SEC_STD_WRITE_DAC,
554                                                     NULL);
555                 if (ret == LDB_SUCCESS) {
556                         flags |= SECINFO_DACL;
557                 }
558                 ret = acl_check_access_on_attribute(module,
559                                                     msg,
560                                                     sd,
561                                                     sid,
562                                                     SEC_FLAG_SYSTEM_SECURITY,
563                                                     NULL);
564                 if (ret == LDB_SUCCESS) {
565                         flags |= SECINFO_SACL;
566                 }
567         }
568         ldb_msg_add_fmt(msg, "sDRightsEffective", "%u", flags);
569         return LDB_SUCCESS;
570 }
571
572 static int acl_add(struct ldb_module *module, struct ldb_request *req)
573 {
574         int ret;
575         struct ldb_dn *parent = ldb_dn_get_parent(req, req->op.add.message->dn);
576         struct ldb_context *ldb;
577         struct ldb_message_element *oc_el;
578         const struct GUID *guid;
579         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
580
581         if (as_system != NULL) {
582                 as_system->critical = 0;
583         }
584
585         if (dsdb_module_am_system(module) || as_system) {
586                 return ldb_next_request(module, req);
587         }
588
589         if (ldb_dn_is_special(req->op.add.message->dn)) {
590                 return ldb_next_request(module, req);
591         }
592         ldb = ldb_module_get_ctx(module);
593         /* Creating an NC. There is probably something we should do here,
594          * but we will establish that later */
595         if ((ldb_dn_compare(req->op.add.message->dn, (ldb_get_schema_basedn(ldb))) == 0) ||
596             (ldb_dn_compare(req->op.add.message->dn, (ldb_get_config_basedn(ldb))) == 0) ||
597             (ldb_dn_compare(req->op.add.message->dn, (ldb_get_root_basedn(ldb))) == 0)) {
598                 return ldb_next_request(module, req);
599         }
600
601         oc_el = ldb_msg_find_element(req->op.add.message, "objectClass");
602         if (!oc_el || oc_el->num_values == 0) {
603                 DEBUG(10,("acl:operation error %s\n", ldb_dn_get_linearized(req->op.add.message->dn)));
604                 return ldb_module_done(req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
605         }
606
607         guid = class_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb),
608                                                       (char *)oc_el->values[oc_el->num_values-1].data);
609         ret = dsdb_check_access_on_dn(ldb, req, parent, SEC_ADS_CREATE_CHILD, guid);
610         if (ret != LDB_SUCCESS) {
611                 return ret;
612         }
613         return ldb_next_request(module, req);
614 }
615
616 static int acl_modify(struct ldb_module *module, struct ldb_request *req)
617 {
618         int ret;
619         struct ldb_context *ldb = ldb_module_get_ctx(module);
620         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
621         unsigned int i;
622         bool modify_sd = false;
623         const struct GUID *guid;
624         uint32_t access_granted;
625         struct object_tree *root = NULL;
626         struct object_tree *new_node = NULL;
627         NTSTATUS status;
628         struct ldb_result *acl_res;
629         struct security_descriptor *sd;
630         struct dom_sid *sid = NULL;
631         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
632         TALLOC_CTX *tmp_ctx = talloc_new(req);
633         static const char *acl_attrs[] = {
634                 "nTSecurityDescriptor",
635                 "objectClass",
636                 "objectSid",
637                 NULL
638         };
639
640         if (as_system != NULL) {
641                 as_system->critical = 0;
642         }
643
644         /* Don't print this debug statement if elements[0].name is going to be NULL */
645         if(req->op.mod.message->num_elements > 0)
646         {
647                 DEBUG(10, ("ldb:acl_modify: %s\n", req->op.mod.message->elements[0].name));
648         }
649         if (dsdb_module_am_system(module) || as_system) {
650                 return ldb_next_request(module, req);
651         }
652         if (ldb_dn_is_special(req->op.mod.message->dn)) {
653                 return ldb_next_request(module, req);
654         }
655         ret = dsdb_module_search_dn(module, req, &acl_res, req->op.mod.message->dn,
656                                     acl_attrs, 0);
657
658         if (ret != LDB_SUCCESS) {
659                 return ret;
660         }
661
662         ret = dsdb_get_sd_from_ldb_message(req, acl_res->msgs[0], &sd);
663         if (ret != LDB_SUCCESS) {
664                 DEBUG(10, ("acl_modify: cannot get descriptor\n"));
665                 return ret;
666         }
667         /* Theoretically we pass the check if the object has no sd */
668         if (!sd) {
669                 return LDB_SUCCESS;
670         }
671
672         guid = get_oc_guid_from_message(module,acl_res->msgs[0]);
673         if (!guid) {
674                 DEBUG(10, ("acl_modify: cannot get guid\n"));
675                 goto fail;
676         }
677
678         ret = dsdb_get_dom_sid_from_ldb_message(req, acl_res->msgs[0], &sid);
679         if (ret != LDB_SUCCESS) {
680                 return LDB_ERR_OPERATIONS_ERROR;
681         }
682
683         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
684                                    &root, &new_node)) {
685                 DEBUG(10, ("acl_modify: cannot add to object tree\n"));
686                 goto fail;
687         }
688         for (i=0; i < req->op.mod.message->num_elements; i++){
689                 const struct dsdb_attribute *attr;
690                 /* clearTextPassword is not in schema */
691                 if (strcmp("clearTextPassword", req->op.mod.message->elements[i].name) == 0) {
692                         attr = dsdb_attribute_by_lDAPDisplayName(schema, "unicodePwd");
693                 } else {
694                         attr = dsdb_attribute_by_lDAPDisplayName(schema,
695                                                                  req->op.mod.message->elements[i].name);
696                 }
697                 if (strcmp("nTSecurityDescriptor", req->op.mod.message->elements[i].name) == 0) {
698                         modify_sd = true;
699                 } else {
700
701                         if (!attr) {
702                                 DEBUG(10, ("acl_modify: cannot find attribute %s\n",
703                                            req->op.mod.message->elements[i].name));
704                                 goto fail;
705                         }
706                         if (!insert_in_object_tree(tmp_ctx,
707                                                    &attr->attributeSecurityGUID, SEC_ADS_WRITE_PROP,
708                                                    &new_node, &new_node)) {
709                                 DEBUG(10, ("acl_modify: cannot add to object tree securityGUID\n"));
710                                 goto fail;
711                         }
712
713                         if (!insert_in_object_tree(tmp_ctx,
714                                                    &attr->schemaIDGUID, SEC_ADS_WRITE_PROP, &new_node, &new_node)) {
715                                 DEBUG(10, ("acl_modify: cannot add to object tree attributeGUID\n"));
716                                 goto fail;
717                         }
718                 }
719         }
720
721         if (root->num_of_children > 0) {
722                 status = sec_access_check_ds(sd, acl_user_token(module),
723                                              SEC_ADS_WRITE_PROP,
724                                              &access_granted,
725                                              root,
726                                              sid);
727
728                 if (!NT_STATUS_IS_OK(status)) {
729                         DEBUG(10, ("Object %s nas no write property access\n",
730                                    ldb_dn_get_linearized(req->op.mod.message->dn)));
731                         dsdb_acl_debug(sd,
732                                   acl_user_token(module),
733                                   req->op.mod.message->dn,
734                                   true,
735                                   10);
736                         talloc_free(tmp_ctx);
737                         return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
738                 }
739         }
740         if (modify_sd) {
741                 status = sec_access_check_ds(sd, acl_user_token(module),
742                                              SEC_STD_WRITE_DAC,
743                                              &access_granted,
744                                              NULL,
745                                              sid);
746
747                 if (!NT_STATUS_IS_OK(status)) {
748                         DEBUG(10, ("Object %s nas no write dacl access\n",
749                                    ldb_dn_get_linearized(req->op.mod.message->dn)));
750                         dsdb_acl_debug(sd,
751                                   acl_user_token(module),
752                                   req->op.mod.message->dn,
753                                   true,
754                                   10);
755                         talloc_free(tmp_ctx);
756                         return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
757                 }
758         }
759
760         talloc_free(tmp_ctx);
761         return ldb_next_request(module, req);
762 fail:
763         talloc_free(tmp_ctx);
764         return LDB_ERR_OPERATIONS_ERROR;
765 }
766
767 /* similar to the modify for the time being.
768  * We need to concider the special delete tree case, though - TODO */
769 static int acl_delete(struct ldb_module *module, struct ldb_request *req)
770 {
771         int ret;
772         struct ldb_dn *parent = ldb_dn_get_parent(req, req->op.del.dn);
773         struct ldb_context *ldb;
774         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
775
776         if (as_system != NULL) {
777                 as_system->critical = 0;
778         }
779
780         DEBUG(10, ("ldb:acl_delete: %s\n", ldb_dn_get_linearized(req->op.del.dn)));
781         if (dsdb_module_am_system(module) || as_system) {
782                 return ldb_next_request(module, req);
783         }
784
785         if (ldb_dn_is_special(req->op.del.dn)) {
786                 return ldb_next_request(module, req);
787         }
788         ldb = ldb_module_get_ctx(module);
789         /* first check if we have delete object right */
790         ret = dsdb_check_access_on_dn(ldb, req, req->op.del.dn, SEC_STD_DELETE, NULL);
791         if (ret == LDB_SUCCESS) {
792                 return ldb_next_request(module, req);
793         }
794
795         /* Nope, we don't have delete object. Lets check if we have delete child on the parent */
796         /* No parent, so check fails */
797         if ((ldb_dn_compare(req->op.del.dn, (ldb_get_schema_basedn(ldb))) == 0) ||
798             (ldb_dn_compare(req->op.del.dn, (ldb_get_config_basedn(ldb))) == 0) ||
799             (ldb_dn_compare(req->op.del.dn, (ldb_get_root_basedn(ldb))) == 0)) {
800                 DEBUG(10,("acl:deleting an NC\n"));
801                 return ldb_module_done(req, NULL, NULL, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS);
802         }
803
804         ret = dsdb_check_access_on_dn(ldb, req, parent, SEC_ADS_DELETE_CHILD, NULL);
805         if (ret != LDB_SUCCESS) {
806                 return ret;
807         }
808         return ldb_next_request(module, req);
809 }
810
811 static int acl_rename(struct ldb_module *module, struct ldb_request *req)
812 {
813         int ret;
814         struct ldb_dn *oldparent = ldb_dn_get_parent(req, req->op.rename.olddn);
815         struct ldb_dn *newparent = ldb_dn_get_parent(req, req->op.rename.newdn);
816         struct ldb_context *ldb;
817         struct security_descriptor *sd = NULL;
818         struct dom_sid *sid = NULL;
819         struct ldb_result *acl_res;
820         const struct GUID *guid;
821         struct object_tree *root = NULL;
822         struct object_tree *new_node = NULL;
823         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
824         TALLOC_CTX *tmp_ctx = talloc_new(req);
825         NTSTATUS status;
826         uint32_t access_granted;
827         const char *rdn_name;
828         static const char *acl_attrs[] = {
829                 "nTSecurityDescriptor",
830                 "objectClass",
831                 "objectSid",
832                 NULL
833         };
834
835         if (as_system != NULL) {
836                 as_system->critical = 0;
837         }
838
839         DEBUG(10, ("ldb:acl_rename: %s\n", ldb_dn_get_linearized(req->op.rename.olddn)));
840         if (dsdb_module_am_system(module) || as_system) {
841                 return ldb_next_request(module, req);
842         }
843         if (ldb_dn_is_special(req->op.rename.olddn)) {
844                 return ldb_next_request(module, req);
845         }
846         ldb = ldb_module_get_ctx(module);
847
848         ret = dsdb_module_search_dn(module, req, &acl_res, req->op.rename.olddn,
849                                     acl_attrs, DSDB_SEARCH_SHOW_DELETED);
850         /* we sould be able to find the parent */
851         if (ret != LDB_SUCCESS) {
852                 DEBUG(10,("acl: failed to find object %s\n",
853                           ldb_dn_get_linearized(req->op.rename.olddn)));
854                 return ret;
855         }
856
857         guid = get_oc_guid_from_message(module,acl_res->msgs[0]);
858         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
859                                    &root, &new_node)) {
860                 return LDB_ERR_OPERATIONS_ERROR;
861         };
862
863         guid = attribute_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb),
864                                                           "name");
865         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
866                                    &new_node, &new_node)) {
867                 return LDB_ERR_OPERATIONS_ERROR;
868         };
869
870         rdn_name = ldb_dn_get_rdn_name(req->op.rename.olddn);
871         if (rdn_name == NULL) {
872                 return LDB_ERR_OPERATIONS_ERROR;
873         }
874         guid = attribute_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb),
875                                                           rdn_name);
876         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
877                                    &new_node, &new_node)) {
878                 return LDB_ERR_OPERATIONS_ERROR;
879         };
880
881         ret = dsdb_get_sd_from_ldb_message(req, acl_res->msgs[0], &sd);
882
883         if (ret != LDB_SUCCESS) {
884                 return LDB_ERR_OPERATIONS_ERROR;
885         }
886         /* Theoretically we pass the check if the object has no sd */
887         if (!sd) {
888                 return LDB_SUCCESS;
889         }
890         ret = dsdb_get_dom_sid_from_ldb_message(req, acl_res->msgs[0], &sid);
891         if (ret != LDB_SUCCESS) {
892                 return LDB_ERR_OPERATIONS_ERROR;
893         }
894
895         status = sec_access_check_ds(sd, acl_user_token(module),
896                                      SEC_ADS_WRITE_PROP,
897                                      &access_granted,
898                                      root,
899                                      sid);
900
901         if (!NT_STATUS_IS_OK(status)) {
902                 DEBUG(10, ("Object %s nas no wp on name\n",
903                            ldb_dn_get_linearized(req->op.rename.olddn)));
904                 dsdb_acl_debug(sd,
905                           acl_user_token(module),
906                           req->op.rename.olddn,
907                           true,
908                           10);
909                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
910         }
911
912         if (ldb_dn_compare(oldparent, newparent) == 0) {
913                 /* regular rename, not move, nothing more to do */
914                 return ldb_next_request(module, req);
915         }
916
917         /* What exactly to do in this case? It would fail anyway.. */
918         if ((ldb_dn_compare(req->op.rename.newdn, (ldb_get_schema_basedn(ldb))) == 0) ||
919             (ldb_dn_compare(req->op.rename.newdn, (ldb_get_config_basedn(ldb))) == 0) ||
920             (ldb_dn_compare(req->op.rename.newdn, (ldb_get_root_basedn(ldb))) == 0)) {
921                 DEBUG(10,("acl:moving as an NC\n"));
922                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
923         }
924         /* new parent should have create child */
925         talloc_free(tmp_ctx);
926         tmp_ctx = talloc_new(req);
927         root = NULL;
928         new_node = NULL;
929         guid = get_oc_guid_from_message(module,acl_res->msgs[0]);
930         if (!guid) {
931                 DEBUG(10,("acl:renamed object has no object class\n"));
932                 return ldb_module_done(req, NULL, NULL,  LDB_ERR_OPERATIONS_ERROR);
933         }
934
935         ret = dsdb_check_access_on_dn(ldb, req, newparent, SEC_ADS_CREATE_CHILD, guid);
936         if (ret != LDB_SUCCESS) {
937                 DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn)));
938                 return ret;
939         }
940         /* do we have delete object on the object? */
941
942         status = sec_access_check_ds(sd, acl_user_token(module),
943                                      SEC_STD_DELETE,
944                                      &access_granted,
945                                      NULL,
946                                      sid);
947
948         if (NT_STATUS_IS_OK(status)) {
949                 return ldb_next_request(module, req);
950         }
951         /* what about delete child on the current parent */
952         ret = dsdb_check_access_on_dn(ldb, req, oldparent, SEC_ADS_DELETE_CHILD, NULL);
953         if (ret != LDB_SUCCESS) {
954                 DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn)));
955                 return ldb_module_done(req, NULL, NULL, ret);
956         }
957         return ldb_next_request(module, req);
958 }
959
960 static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares)
961 {
962         struct ldb_context *ldb;
963         struct acl_context *ac;
964         struct acl_private *data;
965         struct ldb_result *acl_res;
966         static const char *acl_attrs[] = {
967                 "objectClass",
968                 "nTSecurityDescriptor",
969                 "objectSid",
970                 NULL
971         };
972         int ret, i;
973
974         ac = talloc_get_type(req->context, struct acl_context);
975         data = talloc_get_type(ldb_module_get_private(ac->module), struct acl_private);
976         ldb = ldb_module_get_ctx(ac->module);
977
978         if (!ares) {
979                 return ldb_module_done(ac->req, NULL, NULL,
980                                        LDB_ERR_OPERATIONS_ERROR);
981         }
982         if (ares->error != LDB_SUCCESS) {
983                 return ldb_module_done(ac->req, ares->controls,
984                                        ares->response, ares->error);
985         }
986
987         switch (ares->type) {
988         case LDB_REPLY_ENTRY:
989                 if (ac->allowedAttributes 
990                     || ac->allowedChildClasses
991                     || ac->allowedChildClassesEffective
992                     || ac->allowedAttributesEffective
993                     || ac->sDRightsEffective) {
994                         ret = dsdb_module_search_dn(ac->module, ac, &acl_res, ares->message->dn, 
995                                                     acl_attrs, 0);
996                         if (ret != LDB_SUCCESS) {
997                                 return ldb_module_done(ac->req, NULL, NULL, ret);
998                         }
999                         if (ac->allowedAttributes || ac->allowedAttributesEffective) {
1000                                 ret = acl_allowedAttributes(ac->module, acl_res->msgs[0], ares->message, ac);
1001                                 if (ret != LDB_SUCCESS) {
1002                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1003                                 }
1004                         }
1005                         if (ac->allowedChildClasses) {
1006                                 ret = acl_childClasses(ac->module, acl_res->msgs[0],
1007                                                        ares->message, "allowedChildClasses");
1008                                 if (ret != LDB_SUCCESS) {
1009                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1010                                 }
1011                         }
1012                         if (ac->allowedChildClassesEffective) {
1013                                 ret = acl_childClassesEffective(ac->module,
1014                                                                 acl_res->msgs[0], ares->message, ac);
1015                                 if (ret != LDB_SUCCESS) {
1016                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1017                                 }
1018                         }
1019                         if (ac->sDRightsEffective) {
1020                                 ret = acl_sDRightsEffective(ac->module,
1021                                                             acl_res->msgs[0], ares->message, ac);
1022                                 if (ret != LDB_SUCCESS) {
1023                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1024                                 }
1025                         }
1026                 }
1027                 if (data && data->password_attrs) {
1028                         if (!ac->am_system) {
1029                                 for (i = 0; data->password_attrs[i]; i++) {
1030                                         ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
1031                                 }
1032                         }
1033                 }
1034                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
1035
1036         case LDB_REPLY_REFERRAL:
1037                 return ldb_module_send_referral(ac->req, ares->referral);
1038
1039         case LDB_REPLY_DONE:
1040                 return ldb_module_done(ac->req, ares->controls,
1041                                        ares->response, LDB_SUCCESS);
1042
1043         }
1044         return LDB_SUCCESS;
1045 }
1046
1047 static int acl_search(struct ldb_module *module, struct ldb_request *req)
1048 {
1049         struct ldb_context *ldb;
1050         struct acl_context *ac;
1051         struct ldb_request *down_req;
1052         struct acl_private *data;
1053         int ret, i;
1054
1055         ldb = ldb_module_get_ctx(module);
1056
1057         ac = talloc_zero(req, struct acl_context);
1058         if (ac == NULL) {
1059                 ldb_oom(ldb);
1060                 return LDB_ERR_OPERATIONS_ERROR;
1061         }
1062         data = talloc_get_type(ldb_module_get_private(module), struct acl_private);
1063
1064         ac->module = module;
1065         ac->req = req;
1066         ac->am_system = dsdb_module_am_system(module);
1067         ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
1068         ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
1069         ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses");
1070         ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
1071         ac->sDRightsEffective = ldb_attr_in_list(req->op.search.attrs, "sDRightsEffective");
1072
1073         /* replace any attributes in the parse tree that are private,
1074            so we don't allow a search for 'userPassword=penguin',
1075            just as we would not allow that attribute to be returned */
1076         if (ac->am_system) {
1077                 /* FIXME: We should copy the tree and keep the original unmodified. */
1078                 /* remove password attributes */
1079                 if (data && data->password_attrs) {
1080                         for (i = 0; data->password_attrs[i]; i++) {
1081                                 ldb_parse_tree_attr_replace(req->op.search.tree,
1082                                                             data->password_attrs[i],
1083                                                             "kludgeACLredactedattribute");
1084                         }
1085                 }
1086         }
1087         ret = ldb_build_search_req_ex(&down_req,
1088                                       ldb, ac,
1089                                       req->op.search.base,
1090                                       req->op.search.scope,
1091                                       req->op.search.tree,
1092                                       req->op.search.attrs,
1093                                       req->controls,
1094                                       ac, acl_search_callback,
1095                                       req);
1096         if (ret != LDB_SUCCESS) {
1097                 return ret;
1098         }
1099         /* perform the search */
1100         return ldb_next_request(module, down_req);
1101 }
1102
1103 _PUBLIC_ const struct ldb_module_ops ldb_acl_module_ops = {
1104         .name              = "acl",
1105         .search            = acl_search,
1106         .add               = acl_add,
1107         .modify            = acl_modify,
1108         .del               = acl_delete,
1109         .rename            = acl_rename,
1110         .init_context      = acl_module_init
1111 };