39b8e9911fb33f528847c2798904662df5ecee6a
[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         struct dsdb_schema *schema;
65 };
66
67 bool is_root_base_dn(struct ldb_context *ldb, struct ldb_dn *dn_to_check)
68 {
69         int result;
70         struct ldb_dn *root_base_dn = ldb_get_root_basedn(ldb);
71         result = ldb_dn_compare(root_base_dn,dn_to_check);
72         return (result==0);
73 }
74
75 static struct security_token *acl_user_token(struct ldb_module *module)
76 {
77         struct ldb_context *ldb = ldb_module_get_ctx(module);
78         struct auth_session_info *session_info
79                 = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
80         if(!session_info) {
81                 return NULL;
82         }
83         return session_info->security_token;
84 }
85
86 /* performs an access check from inside the module stack
87  * given the dn of the object to be checked, the required access
88  * guid is either the guid of the extended right, or NULL
89  */
90
91 int dsdb_module_check_access_on_dn(struct ldb_module *module,
92                                    TALLOC_CTX *mem_ctx,
93                                    struct ldb_dn *dn,
94                                    uint32_t access,
95                                    const struct GUID *guid)
96 {
97         int ret;
98         struct ldb_result *acl_res;
99         static const char *acl_attrs[] = {
100                 "nTSecurityDescriptor",
101                 "objectSid",
102                 NULL
103         };
104         struct ldb_context *ldb = ldb_module_get_ctx(module);
105         struct auth_session_info *session_info
106                 = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
107         if(!session_info) {
108                 return ldb_operr(ldb);
109         }
110         ret = dsdb_module_search_dn(module, mem_ctx, &acl_res, dn,
111                                     acl_attrs,
112                                     DSDB_FLAG_NEXT_MODULE |
113                                     DSDB_SEARCH_SHOW_DELETED);
114         if (ret != LDB_SUCCESS) {
115                 DEBUG(10,("access_check: failed to find object %s\n", ldb_dn_get_linearized(dn)));
116                 return ret;
117         }
118         return dsdb_check_access_on_dn_internal(ldb, acl_res,
119                                                 mem_ctx,
120                                                 session_info->security_token,
121                                                 dn,
122                                                 access,
123                                                 guid);
124 }
125
126
127 static int acl_module_init(struct ldb_module *module)
128 {
129         struct ldb_context *ldb;
130         struct acl_private *data;
131         int ret, i;
132         TALLOC_CTX *mem_ctx;
133         static const char *attrs[] = { "passwordAttribute", NULL };
134         struct ldb_result *res;
135         struct ldb_message *msg;
136         struct ldb_message_element *password_attributes;
137
138         ldb = ldb_module_get_ctx(module);
139
140         ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
141         if (ret != LDB_SUCCESS) {
142                 ldb_debug(ldb, LDB_DEBUG_ERROR,
143                           "acl_module_init: Unable to register control with rootdse!\n");
144                 return ldb_operr(ldb);
145         }
146
147         data = talloc(module, struct acl_private);
148         if (data == NULL) {
149                 return ldb_oom(ldb);
150         }
151
152         data->password_attrs = NULL;
153         data->acl_perform = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"),
154                                          NULL, "acl", "perform", false);
155         ldb_module_set_private(module, data);
156
157         mem_ctx = talloc_new(module);
158         if (!mem_ctx) {
159                 return ldb_oom(ldb);
160         }
161
162         ret = dsdb_module_search_dn(module, mem_ctx, &res,
163                                     ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"),
164                                     attrs,
165                                     DSDB_FLAG_NEXT_MODULE);
166         if (ret != LDB_SUCCESS) {
167                 goto done;
168         }
169         if (res->count == 0) {
170                 goto done;
171         }
172
173         if (res->count > 1) {
174                 talloc_free(mem_ctx);
175                 return LDB_ERR_CONSTRAINT_VIOLATION;
176         }
177
178         msg = res->msgs[0];
179
180         password_attributes = ldb_msg_find_element(msg, "passwordAttribute");
181         if (!password_attributes) {
182                 goto done;
183         }
184         data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1);
185         if (!data->password_attrs) {
186                 talloc_free(mem_ctx);
187                 return ldb_oom(ldb);
188         }
189         for (i=0; i < password_attributes->num_values; i++) {
190                 data->password_attrs[i] = (const char *)password_attributes->values[i].data;
191                 talloc_steal(data->password_attrs, password_attributes->values[i].data);
192         }
193         data->password_attrs[i] = NULL;
194
195 done:
196         talloc_free(mem_ctx);
197         return ldb_next_init(module);
198 }
199
200 static const struct GUID *get_oc_guid_from_message(struct ldb_module *module,
201                                                    const struct dsdb_schema *schema,
202                                                    struct ldb_message *msg)
203 {
204         struct ldb_message_element *oc_el;
205
206         oc_el = ldb_msg_find_element(msg, "objectClass");
207         if (!oc_el) {
208                 return NULL;
209         }
210
211         return class_schemaid_guid_by_lDAPDisplayName(schema,
212                                                       (char *)oc_el->values[oc_el->num_values-1].data);
213 }
214
215 static int acl_check_access_on_attribute(struct ldb_module *module,
216                                          TALLOC_CTX *mem_ctx,
217                                          struct security_descriptor *sd,
218                                          struct dom_sid *rp_sid,
219                                          uint32_t access,
220                                          const struct dsdb_attribute *attr)
221 {
222         int ret;
223         NTSTATUS status;
224         uint32_t access_granted;
225         struct object_tree *root = NULL;
226         struct object_tree *new_node = NULL;
227         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
228         struct security_token *token = acl_user_token(module);
229         if (attr) {
230                 if (!GUID_all_zero(&attr->attributeSecurityGUID)) {
231                         if (!insert_in_object_tree(tmp_ctx,
232                                                    &attr->attributeSecurityGUID, access,
233                                                    &root, &new_node)) {
234                                 DEBUG(10, ("acl_search: cannot add to object tree securityGUID\n"));
235                                 goto fail;
236                         }
237
238                         if (!insert_in_object_tree(tmp_ctx,
239                                                    &attr->schemaIDGUID, access, &new_node, &new_node)) {
240                                 DEBUG(10, ("acl_search: cannot add to object tree attributeGUID\n"));
241                                 goto fail;
242                         }
243                 }
244                 else {
245                         if (!insert_in_object_tree(tmp_ctx,
246                                                    &attr->schemaIDGUID, access, &root, &new_node)) {
247                                 DEBUG(10, ("acl_search: cannot add to object tree attributeGUID\n"));
248                                 goto fail;
249                         }
250                 }
251         }
252         status = sec_access_check_ds(sd, token,
253                                      access,
254                                      &access_granted,
255                                      root,
256                                      rp_sid);
257         if (!NT_STATUS_IS_OK(status)) {
258                 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
259         }
260         else {
261                 ret = LDB_SUCCESS;
262         }
263         talloc_free(tmp_ctx);
264         return ret;
265 fail:
266         talloc_free(tmp_ctx);
267         return ldb_operr(ldb_module_get_ctx(module));
268 }
269
270 static int acl_check_access_on_class(struct ldb_module *module,
271                                      const struct dsdb_schema *schema,
272                                      TALLOC_CTX *mem_ctx,
273                                      struct security_descriptor *sd,
274                                      struct dom_sid *rp_sid,
275                                      uint32_t access,
276                                      const char *class_name)
277 {
278         int ret;
279         NTSTATUS status;
280         uint32_t access_granted;
281         struct object_tree *root = NULL;
282         struct object_tree *new_node = NULL;
283         const struct GUID *guid;
284         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
285         struct security_token *token = acl_user_token(module);
286         if (class_name) {
287                 guid = class_schemaid_guid_by_lDAPDisplayName(schema, class_name);
288                 if (!guid) {
289                         DEBUG(10, ("acl_search: cannot find class %s\n",
290                                    class_name));
291                         goto fail;
292                 }
293                 if (!insert_in_object_tree(tmp_ctx,
294                                            guid, access,
295                                            &root, &new_node)) {
296                         DEBUG(10, ("acl_search: cannot add to object tree guid\n"));
297                         goto fail;
298                 }
299         }
300         status = sec_access_check_ds(sd, token,
301                                      access,
302                                      &access_granted,
303                                      root,
304                                      rp_sid);
305         if (!NT_STATUS_IS_OK(status)) {
306                 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
307         }
308         else {
309                 ret = LDB_SUCCESS;
310         }
311         return ret;
312 fail:
313         return ldb_operr(ldb_module_get_ctx(module));
314 }
315
316 static int acl_allowedAttributes(struct ldb_module *module,
317                                  const struct dsdb_schema *schema,
318                                  struct ldb_message *sd_msg,
319                                  struct ldb_message *msg,
320                                  struct acl_context *ac)
321 {
322         struct ldb_message_element *oc_el;
323         struct ldb_context *ldb = ldb_module_get_ctx(module);
324         TALLOC_CTX *mem_ctx;
325         const char **attr_list;
326         int i, ret;
327
328         /* If we don't have a schema yet, we can't do anything... */
329         if (schema == NULL) {
330                 ldb_asprintf_errstring(ldb, "cannot add allowedAttributes to %s because no schema is loaded", ldb_dn_get_linearized(msg->dn));
331                 return LDB_ERR_OPERATIONS_ERROR;
332         }
333
334         /* Must remove any existing attribute */
335         if (ac->allowedAttributes) {
336                 ldb_msg_remove_attr(msg, "allowedAttributes");
337         }
338
339         mem_ctx = talloc_new(msg);
340         if (!mem_ctx) {
341                 return ldb_oom(ldb);
342         }
343
344         oc_el = ldb_msg_find_element(sd_msg, "objectClass");
345         attr_list = dsdb_full_attribute_list(mem_ctx, schema, oc_el, DSDB_SCHEMA_ALL);
346         if (!attr_list) {
347                 ldb_asprintf_errstring(ldb, "acl: Failed to get list of attributes");
348                 talloc_free(mem_ctx);
349                 return LDB_ERR_OPERATIONS_ERROR;
350         }
351         if (ac->allowedAttributes) {
352                 for (i=0; attr_list && attr_list[i]; i++) {
353                         ldb_msg_add_string(msg, "allowedAttributes", attr_list[i]);
354                 }
355         }
356         if (ac->allowedAttributesEffective) {
357                 struct security_descriptor *sd;
358                 struct dom_sid *sid = NULL;
359                 struct ldb_control *as_system = ldb_request_get_control(ac->req,
360                                                                         LDB_CONTROL_AS_SYSTEM_OID);
361
362                 if (as_system != NULL) {
363                         as_system->critical = 0;
364                 }
365
366                 ldb_msg_remove_attr(msg, "allowedAttributesEffective");
367                 if (ac->am_system || as_system) {
368                         for (i=0; attr_list && attr_list[i]; i++) {
369                                 ldb_msg_add_string(msg, "allowedAttributesEffective", attr_list[i]);
370                         }
371                         return LDB_SUCCESS;
372                 }
373
374                 ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), mem_ctx, sd_msg, &sd);
375
376                 if (ret != LDB_SUCCESS) {
377                         return ret;
378                 }
379
380                 sid = samdb_result_dom_sid(mem_ctx, sd_msg, "objectSid");
381                 for (i=0; attr_list && attr_list[i]; i++) {
382                         const struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema,
383                                                                                         attr_list[i]);
384                         if (!attr) {
385                                 return ldb_operr(ldb);
386                         }
387                         /* remove constructed attributes */
388                         if (attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED
389                             || attr->systemOnly
390                             || (attr->linkID != 0 && attr->linkID % 2 != 0 )) {
391                                 continue;
392                         }
393                         ret = acl_check_access_on_attribute(module,
394                                                             msg,
395                                                             sd,
396                                                             sid,
397                                                             SEC_ADS_WRITE_PROP,
398                                                             attr);
399                         if (ret == LDB_SUCCESS) {
400                                 ldb_msg_add_string(msg, "allowedAttributesEffective", attr_list[i]);
401                         }
402                 }
403         }
404         return LDB_SUCCESS;
405 }
406
407 static int acl_childClasses(struct ldb_module *module,
408                             const struct dsdb_schema *schema,
409                             struct ldb_message *sd_msg,
410                             struct ldb_message *msg,
411                             const char *attrName)
412 {
413         struct ldb_message_element *oc_el;
414         struct ldb_message_element *allowedClasses;
415         const struct dsdb_class *sclass;
416         unsigned int i, j;
417         int ret;
418
419         /* If we don't have a schema yet, we can't do anything... */
420         if (schema == NULL) {
421                 ldb_asprintf_errstring(ldb_module_get_ctx(module), "cannot add childClassesEffective to %s because no schema is loaded", ldb_dn_get_linearized(msg->dn));
422                 return LDB_ERR_OPERATIONS_ERROR;
423         }
424
425         /* Must remove any existing attribute, or else confusion reins */
426         ldb_msg_remove_attr(msg, attrName);
427         ret = ldb_msg_add_empty(msg, attrName, 0, &allowedClasses);
428         if (ret != LDB_SUCCESS) {
429                 return ret;
430         }
431
432         oc_el = ldb_msg_find_element(sd_msg, "objectClass");
433
434         for (i=0; oc_el && i < oc_el->num_values; i++) {
435                 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
436                 if (!sclass) {
437                         /* We don't know this class?  what is going on? */
438                         continue;
439                 }
440
441                 for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
442                         ldb_msg_add_string(msg, attrName, sclass->possibleInferiors[j]);
443                 }
444         }
445         if (allowedClasses->num_values > 1) {
446                 TYPESAFE_QSORT(allowedClasses->values, allowedClasses->num_values, data_blob_cmp);
447                 for (i=1 ; i < allowedClasses->num_values; i++) {
448                         struct ldb_val *val1 = &allowedClasses->values[i-1];
449                         struct ldb_val *val2 = &allowedClasses->values[i];
450                         if (data_blob_cmp(val1, val2) == 0) {
451                                 memmove(val1, val2, (allowedClasses->num_values - i) * sizeof(struct ldb_val));
452                                 allowedClasses->num_values--;
453                                 i--;
454                         }
455                 }
456         }
457
458         return LDB_SUCCESS;
459 }
460
461 static int acl_childClassesEffective(struct ldb_module *module,
462                                      const struct dsdb_schema *schema,
463                                      struct ldb_message *sd_msg,
464                                      struct ldb_message *msg,
465                                      struct acl_context *ac)
466 {
467         struct ldb_message_element *oc_el;
468         struct ldb_message_element *allowedClasses = NULL;
469         const struct dsdb_class *sclass;
470         struct security_descriptor *sd;
471         struct ldb_control *as_system = ldb_request_get_control(ac->req,
472                                                                 LDB_CONTROL_AS_SYSTEM_OID);
473         struct dom_sid *sid = NULL;
474         unsigned int i, j;
475         int ret;
476
477         if (as_system != NULL) {
478                 as_system->critical = 0;
479         }
480
481         if (ac->am_system || as_system) {
482                 return acl_childClasses(module, schema, sd_msg, msg, "allowedChildClassesEffective");
483         }
484
485         /* If we don't have a schema yet, we can't do anything... */
486         if (schema == NULL) {
487                 ldb_asprintf_errstring(ldb_module_get_ctx(module), "cannot add allowedChildClassesEffective to %s because no schema is loaded", ldb_dn_get_linearized(msg->dn));
488                 return LDB_ERR_OPERATIONS_ERROR;
489         }
490
491         /* Must remove any existing attribute, or else confusion reins */
492         ldb_msg_remove_attr(msg, "allowedChildClassesEffective");
493
494         oc_el = ldb_msg_find_element(sd_msg, "objectClass");
495         ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), msg, sd_msg, &sd);
496         if (ret != LDB_SUCCESS) {
497                 return ret;
498         }
499
500         sid = samdb_result_dom_sid(msg, sd_msg, "objectSid");
501         for (i=0; oc_el && i < oc_el->num_values; i++) {
502                 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
503                 if (!sclass) {
504                         /* We don't know this class?  what is going on? */
505                         continue;
506                 }
507
508                 for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
509                         ret = acl_check_access_on_class(module,
510                                                         schema,
511                                                         msg,
512                                                         sd,
513                                                         sid,
514                                                         SEC_ADS_CREATE_CHILD,
515                                                         sclass->possibleInferiors[j]);
516                         if (ret == LDB_SUCCESS) {
517                                 ldb_msg_add_string(msg, "allowedChildClassesEffective",
518                                                    sclass->possibleInferiors[j]);
519                         }
520                 }
521         }
522         allowedClasses = ldb_msg_find_element(msg, "allowedChildClassesEffective");
523         if (!allowedClasses) {
524                 return LDB_SUCCESS;
525         }
526
527         if (allowedClasses->num_values > 1) {
528                 TYPESAFE_QSORT(allowedClasses->values, allowedClasses->num_values, data_blob_cmp);
529                 for (i=1 ; i < allowedClasses->num_values; i++) {
530                         struct ldb_val *val1 = &allowedClasses->values[i-1];
531                         struct ldb_val *val2 = &allowedClasses->values[i];
532                         if (data_blob_cmp(val1, val2) == 0) {
533                                 memmove(val1, val2, (allowedClasses->num_values - i) * sizeof( struct ldb_val));
534                                 allowedClasses->num_values--;
535                                 i--;
536                         }
537                 }
538         }
539         return LDB_SUCCESS;
540 }
541
542 static int acl_sDRightsEffective(struct ldb_module *module,
543                                  struct ldb_message *sd_msg,
544                                  struct ldb_message *msg,
545                                  struct acl_context *ac)
546 {
547         struct ldb_message_element *rightsEffective;
548         int ret;
549         struct security_descriptor *sd;
550         struct ldb_control *as_system = ldb_request_get_control(ac->req,
551                                                                 LDB_CONTROL_AS_SYSTEM_OID);
552         struct dom_sid *sid = NULL;
553         uint32_t flags = 0;
554
555         if (as_system != NULL) {
556                 as_system->critical = 0;
557         }
558
559         /* Must remove any existing attribute, or else confusion reins */
560         ldb_msg_remove_attr(msg, "sDRightsEffective");
561         ret = ldb_msg_add_empty(msg, "sDRightsEffective", 0, &rightsEffective);
562         if (ret != LDB_SUCCESS) {
563                 return ret;
564         }
565         if (ac->am_system || as_system) {
566                 flags = SECINFO_OWNER | SECINFO_GROUP |  SECINFO_SACL |  SECINFO_DACL;
567         }
568         else {
569                 /* Get the security descriptor from the message */
570                 ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(module), msg, sd_msg, &sd);
571                 if (ret != LDB_SUCCESS) {
572                         return ret;
573                 }
574                 sid = samdb_result_dom_sid(msg, sd_msg, "objectSid");
575                 ret = acl_check_access_on_attribute(module,
576                                                     msg,
577                                                     sd,
578                                                     sid,
579                                                     SEC_STD_WRITE_OWNER,
580                                                     NULL);
581                 if (ret == LDB_SUCCESS) {
582                         flags |= SECINFO_OWNER | SECINFO_GROUP;
583                 }
584                 ret = acl_check_access_on_attribute(module,
585                                                     msg,
586                                                     sd,
587                                                     sid,
588                                                     SEC_STD_WRITE_DAC,
589                                                     NULL);
590                 if (ret == LDB_SUCCESS) {
591                         flags |= SECINFO_DACL;
592                 }
593                 ret = acl_check_access_on_attribute(module,
594                                                     msg,
595                                                     sd,
596                                                     sid,
597                                                     SEC_FLAG_SYSTEM_SECURITY,
598                                                     NULL);
599                 if (ret == LDB_SUCCESS) {
600                         flags |= SECINFO_SACL;
601                 }
602         }
603         ldb_msg_add_fmt(msg, "sDRightsEffective", "%u", flags);
604         return LDB_SUCCESS;
605 }
606
607 static int acl_add(struct ldb_module *module, struct ldb_request *req)
608 {
609         int ret;
610         struct ldb_dn *parent = ldb_dn_get_parent(req, req->op.add.message->dn);
611         struct ldb_context *ldb;
612         const struct dsdb_schema *schema;
613         struct ldb_message_element *oc_el;
614         const struct GUID *guid;
615         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
616
617         if (as_system != NULL) {
618                 as_system->critical = 0;
619         }
620
621         if (dsdb_module_am_system(module) || as_system) {
622                 return ldb_next_request(module, req);
623         }
624
625         if (ldb_dn_is_special(req->op.add.message->dn)) {
626                 return ldb_next_request(module, req);
627         }
628         ldb = ldb_module_get_ctx(module);
629         /* Creating an NC. There is probably something we should do here,
630          * but we will establish that later */
631         /* FIXME: this has to be made dynamic at some point */
632         if ((ldb_dn_compare(req->op.add.message->dn, (ldb_get_schema_basedn(ldb))) == 0) ||
633             (ldb_dn_compare(req->op.add.message->dn, (ldb_get_config_basedn(ldb))) == 0) ||
634             (ldb_dn_compare(req->op.add.message->dn, (ldb_get_default_basedn(ldb))) == 0) ||
635             (ldb_dn_compare(req->op.add.message->dn, (ldb_get_root_basedn(ldb))) == 0)) {
636                 return ldb_next_request(module, req);
637         }
638
639         schema = dsdb_get_schema(ldb, req);
640         if (!schema) {
641                 return ldb_operr(ldb);
642         }
643
644         oc_el = ldb_msg_find_element(req->op.add.message, "objectClass");
645         if (!oc_el || oc_el->num_values == 0) {
646                 DEBUG(10,("acl:operation error %s\n", ldb_dn_get_linearized(req->op.add.message->dn)));
647                 return ldb_module_done(req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
648         }
649
650         guid = class_schemaid_guid_by_lDAPDisplayName(schema,
651                                                       (char *)oc_el->values[oc_el->num_values-1].data);
652         ret = dsdb_module_check_access_on_dn(module, req, parent, SEC_ADS_CREATE_CHILD, guid);
653         if (ret != LDB_SUCCESS) {
654                 return ret;
655         }
656         return ldb_next_request(module, req);
657 }
658
659 /* checks for validated writes */
660 static int acl_check_extended_right(TALLOC_CTX *mem_ctx,
661                                     struct security_descriptor *sd,
662                                     struct security_token *token,
663                                     const char *ext_right,
664                                     uint32_t right_type,
665                                     struct dom_sid *sid)
666 {
667         struct GUID right;
668         NTSTATUS status;
669         uint32_t access_granted;
670         struct object_tree *root = NULL;
671         struct object_tree *new_node = NULL;
672         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
673
674         GUID_from_string(ext_right, &right);
675
676         if (!insert_in_object_tree(tmp_ctx, &right, right_type,
677                                    &root, &new_node)) {
678                 DEBUG(10, ("acl_ext_right: cannot add to object tree\n"));
679                 talloc_free(tmp_ctx);
680                 return LDB_ERR_OPERATIONS_ERROR;
681         }
682         status = sec_access_check_ds(sd, token,
683                                      right_type,
684                                      &access_granted,
685                                      root,
686                                      sid);
687
688         if (!NT_STATUS_IS_OK(status)) {
689                 talloc_free(tmp_ctx);
690                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
691         }
692         talloc_free(tmp_ctx);
693         return LDB_SUCCESS;
694 }
695
696
697 /* ckecks if modifications are allowed on "Member" attribute */
698 static int acl_check_self_membership(TALLOC_CTX *mem_ctx,
699                                      struct ldb_module *module,
700                                      struct ldb_request *req,
701                                      struct security_descriptor *sd,
702                                      struct dom_sid *sid,
703                                      const struct GUID *oc_guid,
704                                      const struct dsdb_attribute *attr)
705 {
706         int ret;
707         unsigned int i;
708         struct ldb_context *ldb = ldb_module_get_ctx(module);
709         struct ldb_dn *user_dn;
710         struct ldb_message_element *member_el;
711         /* if we have wp, we can do whatever we like */
712         if (acl_check_access_on_attribute(module,
713                                           mem_ctx,
714                                           sd,
715                                           sid,
716                                           SEC_ADS_WRITE_PROP,
717                                           attr) == LDB_SUCCESS) {
718                 return LDB_SUCCESS;
719         }
720         /* if we are adding/deleting ourselves, check for self membership */
721         ret = dsdb_find_dn_by_sid(ldb, mem_ctx, acl_user_token(module)->user_sid, &user_dn);
722         if (ret != LDB_SUCCESS) {
723                 return ret;
724         }
725         member_el = ldb_msg_find_element(req->op.mod.message, "member");
726         if (!member_el) {
727                 return ldb_operr(ldb);
728         }
729         /* user can only remove oneself */
730         if (member_el->num_values == 0) {
731                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
732         }
733         for (i = 0; i < member_el->num_values; i++) {
734                 if (strcasecmp((const char *)member_el->values[i].data,
735                                ldb_dn_get_extended_linearized(mem_ctx, user_dn, 1)) != 0) {
736                         return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
737                 }
738         }
739         ret = acl_check_extended_right(mem_ctx, sd, acl_user_token(module),
740                                        GUID_DRS_SELF_MEMBERSHIP,
741                                        SEC_ADS_SELF_WRITE,
742                                        sid);
743         if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
744                 dsdb_acl_debug(sd, acl_user_token(module),
745                                req->op.mod.message->dn,
746                                true,
747                                10);
748         }
749         return ret;
750 }
751
752 static int acl_check_password_rights(TALLOC_CTX *mem_ctx,
753                                      struct ldb_module *module,
754                                      struct ldb_request *req,
755                                      struct security_descriptor *sd,
756                                      struct dom_sid *sid,
757                                      const struct GUID *oc_guid)
758 {
759         int ret = LDB_SUCCESS;
760         unsigned int del_attr_cnt = 0, add_attr_cnt = 0, rep_attr_cnt = 0;
761         struct ldb_message_element *el;
762         struct ldb_message *msg;
763         const char *passwordAttrs[] = { "userPassword", "unicodePwd",
764                                         "clearTextPassword", NULL }, **l;
765         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
766
767         msg = ldb_msg_copy_shallow(tmp_ctx, req->op.mod.message);
768         if (msg == NULL) {
769                 return ldb_module_oom(module);
770         }
771         for (l = passwordAttrs; *l != NULL; l++) {
772                 while ((el = ldb_msg_find_element(msg, *l)) != NULL) {
773                         if (el->flags == LDB_FLAG_MOD_DELETE) {
774                                 ++del_attr_cnt;
775                         }
776                         if (el->flags == LDB_FLAG_MOD_ADD) {
777                                 ++add_attr_cnt;
778                         }
779                         if (el->flags == LDB_FLAG_MOD_REPLACE) {
780                                 ++rep_attr_cnt;
781                         }
782                         ldb_msg_remove_element(msg, el);
783                 }
784         }
785         /* a single delete will be handled by password hash
786            later in the stack, so we let it though here */
787         if (del_attr_cnt > 0 && add_attr_cnt == 0) {
788                 talloc_free(tmp_ctx);
789                 return LDB_SUCCESS;
790         }
791         if (rep_attr_cnt > 0 || (add_attr_cnt != del_attr_cnt)) {
792                 ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
793                                                GUID_DRS_FORCE_CHANGE_PASSWORD,
794                                                SEC_ADS_CONTROL_ACCESS,
795                                                sid);
796         }
797         else if (add_attr_cnt == 1 && del_attr_cnt == 1) {
798                 ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
799                                                GUID_DRS_USER_CHANGE_PASSWORD,
800                                                SEC_ADS_CONTROL_ACCESS,
801                                                sid);
802                 /* Very strange, but we get constraint violation in this case */
803                 if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
804                         ret = LDB_ERR_CONSTRAINT_VIOLATION;
805                 }
806         }
807         if (ret != LDB_SUCCESS) {
808                 dsdb_acl_debug(sd, acl_user_token(module),
809                                req->op.mod.message->dn,
810                                true,
811                                10);
812         }
813         talloc_free(tmp_ctx);
814         return ret;
815 }
816
817 static int acl_modify(struct ldb_module *module, struct ldb_request *req)
818 {
819         int ret;
820         struct ldb_context *ldb = ldb_module_get_ctx(module);
821         const struct dsdb_schema *schema;
822         unsigned int i;
823         const struct GUID *guid;
824         uint32_t access_granted;
825         struct object_tree *root = NULL;
826         struct object_tree *new_node = NULL;
827         NTSTATUS status;
828         struct ldb_result *acl_res;
829         struct security_descriptor *sd;
830         struct dom_sid *sid = NULL;
831         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
832         TALLOC_CTX *tmp_ctx = talloc_new(req);
833         static const char *acl_attrs[] = {
834                 "nTSecurityDescriptor",
835                 "objectClass",
836                 "objectSid",
837                 NULL
838         };
839
840         if (as_system != NULL) {
841                 as_system->critical = 0;
842         }
843
844         /* Don't print this debug statement if elements[0].name is going to be NULL */
845         if(req->op.mod.message->num_elements > 0)
846         {
847                 DEBUG(10, ("ldb:acl_modify: %s\n", req->op.mod.message->elements[0].name));
848         }
849         if (dsdb_module_am_system(module) || as_system) {
850                 return ldb_next_request(module, req);
851         }
852         if (ldb_dn_is_special(req->op.mod.message->dn)) {
853                 return ldb_next_request(module, req);
854         }
855         ret = dsdb_module_search_dn(module, tmp_ctx, &acl_res, req->op.mod.message->dn,
856                                     acl_attrs,
857                                     DSDB_FLAG_NEXT_MODULE);
858
859         if (ret != LDB_SUCCESS) {
860                 goto fail;
861         }
862
863         schema = dsdb_get_schema(ldb, tmp_ctx);
864         if (!schema) {
865                 ret = LDB_ERR_OPERATIONS_ERROR;
866                 goto fail;
867         }
868
869         ret = dsdb_get_sd_from_ldb_message(ldb, tmp_ctx, acl_res->msgs[0], &sd);
870         if (ret != LDB_SUCCESS) {
871                 DEBUG(10, ("acl_modify: cannot get descriptor\n"));
872                 goto fail;
873         }
874         /* Theoretically we pass the check if the object has no sd */
875         if (!sd) {
876                 goto success;
877         }
878
879         guid = get_oc_guid_from_message(module, schema, acl_res->msgs[0]);
880         if (!guid) {
881                 DEBUG(10, ("acl_modify: cannot get guid\n"));
882                 goto fail;
883         }
884         sid = samdb_result_dom_sid(req, acl_res->msgs[0], "objectSid");
885         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
886                                    &root, &new_node)) {
887                 DEBUG(10, ("acl_modify: cannot add to object tree\n"));
888                 goto fail;
889         }
890         for (i=0; i < req->op.mod.message->num_elements; i++){
891                 const struct dsdb_attribute *attr;
892                 attr = dsdb_attribute_by_lDAPDisplayName(schema,
893                                                                  req->op.mod.message->elements[i].name);
894
895                 if (ldb_attr_cmp("nTSecurityDescriptor", req->op.mod.message->elements[i].name) == 0) {
896                         status = sec_access_check_ds(sd, acl_user_token(module),
897                                              SEC_STD_WRITE_DAC,
898                                              &access_granted,
899                                              NULL,
900                                              sid);
901
902                         if (!NT_STATUS_IS_OK(status)) {
903                                 DEBUG(10, ("Object %s has no write dacl access\n",
904                                            ldb_dn_get_linearized(req->op.mod.message->dn)));
905                                 dsdb_acl_debug(sd,
906                                                acl_user_token(module),
907                                                req->op.mod.message->dn,
908                                                true,
909                                                10);
910                                 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
911                                 goto fail;
912                         }
913                 }
914                 else if (ldb_attr_cmp("member", req->op.mod.message->elements[i].name) == 0) {
915                         ret = acl_check_self_membership(tmp_ctx,
916                                                         module,
917                                                         req,
918                                                         sd,
919                                                         sid,
920                                                         guid,
921                                                         attr);
922                         if (ret != LDB_SUCCESS) {
923                                 goto fail;
924                         }
925                 }
926                 else if (ldb_attr_cmp("dBCSPwd", req->op.mod.message->elements[i].name) == 0) {
927                         /* this one is not affected by any rights, we should let it through
928                            so that passwords_hash returns the correct error */
929                         continue;
930                 }
931                 else if (ldb_attr_cmp("unicodePwd", req->op.mod.message->elements[i].name) == 0 ||
932                          ldb_attr_cmp("userPassword", req->op.mod.message->elements[i].name) == 0 ||
933                          ldb_attr_cmp("clearTextPassword", req->op.mod.message->elements[i].name) == 0) {
934                         ret = acl_check_password_rights(tmp_ctx,
935                                                         module,
936                                                         req,
937                                                         sd,
938                                                         sid,
939                                                         guid);
940                         if (ret != LDB_SUCCESS) {
941                                 goto fail;
942                         }
943                 } else {
944
945                 /* This basic attribute existence check with the right errorcode
946                  * is needed since this module is the first one which requests
947                  * schema attribute informations.
948                  * The complete attribute checking is done in the
949                  * "objectclass_attrs" module behind this one.
950                  */
951                         if (!attr) {
952                                 ldb_asprintf_errstring(ldb, "acl_modify: attribute '%s' on entry '%s' was not found in the schema!",
953                                                        req->op.mod.message->elements[i].name,
954                                                ldb_dn_get_linearized(req->op.mod.message->dn));
955                                 ret =  LDB_ERR_NO_SUCH_ATTRIBUTE;
956                                 goto fail;
957                         }
958                         if (!insert_in_object_tree(tmp_ctx,
959                                                    &attr->attributeSecurityGUID, SEC_ADS_WRITE_PROP,
960                                                    &new_node, &new_node)) {
961                                 DEBUG(10, ("acl_modify: cannot add to object tree securityGUID\n"));
962                                 ret = LDB_ERR_OPERATIONS_ERROR;
963                                 goto fail;
964                         }
965
966                         if (!insert_in_object_tree(tmp_ctx,
967                                                    &attr->schemaIDGUID, SEC_ADS_WRITE_PROP, &new_node, &new_node)) {
968                                 DEBUG(10, ("acl_modify: cannot add to object tree attributeGUID\n"));
969                                 ret = LDB_ERR_OPERATIONS_ERROR;
970                                 goto fail;
971                         }
972                 }
973         }
974
975         if (root->num_of_children > 0) {
976                 status = sec_access_check_ds(sd, acl_user_token(module),
977                                              SEC_ADS_WRITE_PROP,
978                                              &access_granted,
979                                              root,
980                                              sid);
981
982                 if (!NT_STATUS_IS_OK(status)) {
983                         DEBUG(10, ("Object %s has no write property access\n",
984                                    ldb_dn_get_linearized(req->op.mod.message->dn)));
985                         dsdb_acl_debug(sd,
986                                   acl_user_token(module),
987                                   req->op.mod.message->dn,
988                                   true,
989                                   10);
990                         ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
991                         goto fail;
992                 }
993         }
994
995 success:
996         talloc_free(tmp_ctx);
997         return ldb_next_request(module, req);
998 fail:
999         talloc_free(tmp_ctx);
1000         return ret;
1001 }
1002
1003 /* similar to the modify for the time being.
1004  * We need to concider the special delete tree case, though - TODO */
1005 static int acl_delete(struct ldb_module *module, struct ldb_request *req)
1006 {
1007         int ret;
1008         struct ldb_dn *parent = ldb_dn_get_parent(req, req->op.del.dn);
1009         struct ldb_context *ldb;
1010         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
1011
1012         if (as_system != NULL) {
1013                 as_system->critical = 0;
1014         }
1015
1016         DEBUG(10, ("ldb:acl_delete: %s\n", ldb_dn_get_linearized(req->op.del.dn)));
1017         if (dsdb_module_am_system(module) || as_system) {
1018                 return ldb_next_request(module, req);
1019         }
1020
1021         if (ldb_dn_is_special(req->op.del.dn)) {
1022                 return ldb_next_request(module, req);
1023         }
1024         ldb = ldb_module_get_ctx(module);
1025         /* first check if we have delete object right */
1026         ret = dsdb_module_check_access_on_dn(module, req, req->op.del.dn, SEC_STD_DELETE, NULL);
1027         if (ret == LDB_SUCCESS) {
1028                 return ldb_next_request(module, req);
1029         }
1030
1031         /* Nope, we don't have delete object. Lets check if we have delete child on the parent */
1032         /* No parent, so check fails */
1033         /* FIXME: this has to be made dynamic at some point */
1034         if ((ldb_dn_compare(req->op.del.dn, (ldb_get_schema_basedn(ldb))) == 0) ||
1035             (ldb_dn_compare(req->op.del.dn, (ldb_get_config_basedn(ldb))) == 0) ||
1036             (ldb_dn_compare(req->op.del.dn, (ldb_get_default_basedn(ldb))) == 0) ||
1037             (ldb_dn_compare(req->op.del.dn, (ldb_get_root_basedn(ldb))) == 0)) {
1038                 DEBUG(10,("acl:deleting an NC\n"));
1039                 return ldb_module_done(req, NULL, NULL, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS);
1040         }
1041
1042         ret = dsdb_module_check_access_on_dn(module, req, parent, SEC_ADS_DELETE_CHILD, NULL);
1043         if (ret != LDB_SUCCESS) {
1044                 return ret;
1045         }
1046         return ldb_next_request(module, req);
1047 }
1048
1049 static int acl_rename(struct ldb_module *module, struct ldb_request *req)
1050 {
1051         int ret;
1052         struct ldb_dn *oldparent = ldb_dn_get_parent(req, req->op.rename.olddn);
1053         struct ldb_dn *newparent = ldb_dn_get_parent(req, req->op.rename.newdn);
1054         const struct dsdb_schema *schema;
1055         struct ldb_context *ldb;
1056         struct security_descriptor *sd = NULL;
1057         struct dom_sid *sid = NULL;
1058         struct ldb_result *acl_res;
1059         const struct GUID *guid;
1060         struct object_tree *root = NULL;
1061         struct object_tree *new_node = NULL;
1062         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
1063         TALLOC_CTX *tmp_ctx = talloc_new(req);
1064         NTSTATUS status;
1065         uint32_t access_granted;
1066         const char *rdn_name;
1067         static const char *acl_attrs[] = {
1068                 "nTSecurityDescriptor",
1069                 "objectClass",
1070                 "objectSid",
1071                 NULL
1072         };
1073
1074         if (as_system != NULL) {
1075                 as_system->critical = 0;
1076         }
1077
1078         DEBUG(10, ("ldb:acl_rename: %s\n", ldb_dn_get_linearized(req->op.rename.olddn)));
1079         if (dsdb_module_am_system(module) || as_system) {
1080                 return ldb_next_request(module, req);
1081         }
1082         if (ldb_dn_is_special(req->op.rename.olddn)) {
1083                 return ldb_next_request(module, req);
1084         }
1085         ldb = ldb_module_get_ctx(module);
1086
1087         ret = dsdb_module_search_dn(module, req, &acl_res, req->op.rename.olddn,
1088                                     acl_attrs,
1089                                     DSDB_FLAG_NEXT_MODULE |
1090                                     DSDB_SEARCH_SHOW_DELETED);
1091         /* we sould be able to find the parent */
1092         if (ret != LDB_SUCCESS) {
1093                 DEBUG(10,("acl: failed to find object %s\n",
1094                           ldb_dn_get_linearized(req->op.rename.olddn)));
1095                 return ret;
1096         }
1097
1098         schema = dsdb_get_schema(ldb, acl_res);
1099         if (!schema) {
1100                 talloc_free(acl_res);
1101                 return ldb_operr(ldb);
1102         }
1103
1104         guid = get_oc_guid_from_message(module, schema, acl_res->msgs[0]);
1105         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
1106                                    &root, &new_node)) {
1107                 return ldb_operr(ldb);
1108         };
1109
1110         guid = attribute_schemaid_guid_by_lDAPDisplayName(schema,
1111                                                           "name");
1112         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
1113                                    &new_node, &new_node)) {
1114                 return ldb_operr(ldb);
1115         };
1116
1117         rdn_name = ldb_dn_get_rdn_name(req->op.rename.olddn);
1118         if (rdn_name == NULL) {
1119                 return ldb_operr(ldb);
1120         }
1121         guid = attribute_schemaid_guid_by_lDAPDisplayName(schema,
1122                                                           rdn_name);
1123         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
1124                                    &new_node, &new_node)) {
1125                 return ldb_operr(ldb);
1126         };
1127
1128         ret = dsdb_get_sd_from_ldb_message(ldb, req, acl_res->msgs[0], &sd);
1129
1130         if (ret != LDB_SUCCESS) {
1131                 return ldb_operr(ldb);
1132         }
1133         /* Theoretically we pass the check if the object has no sd */
1134         if (!sd) {
1135                 return LDB_SUCCESS;
1136         }
1137         sid = samdb_result_dom_sid(req, acl_res->msgs[0], "objectSid");
1138         status = sec_access_check_ds(sd, acl_user_token(module),
1139                                      SEC_ADS_WRITE_PROP,
1140                                      &access_granted,
1141                                      root,
1142                                      sid);
1143
1144         if (!NT_STATUS_IS_OK(status)) {
1145                 DEBUG(10, ("Object %s has no wp on name\n",
1146                            ldb_dn_get_linearized(req->op.rename.olddn)));
1147                 dsdb_acl_debug(sd,
1148                           acl_user_token(module),
1149                           req->op.rename.olddn,
1150                           true,
1151                           10);
1152                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
1153         }
1154
1155         if (ldb_dn_compare(oldparent, newparent) == 0) {
1156                 /* regular rename, not move, nothing more to do */
1157                 return ldb_next_request(module, req);
1158         }
1159
1160         /* What exactly to do in this case? It would fail anyway.. */
1161         /* FIXME: this has to be made dynamic at some point */
1162         if ((ldb_dn_compare(req->op.rename.newdn, (ldb_get_schema_basedn(ldb))) == 0) ||
1163             (ldb_dn_compare(req->op.rename.newdn, (ldb_get_config_basedn(ldb))) == 0) ||
1164             (ldb_dn_compare(req->op.rename.newdn, (ldb_get_default_basedn(ldb))) == 0) ||
1165             (ldb_dn_compare(req->op.rename.newdn, (ldb_get_root_basedn(ldb))) == 0)) {
1166                 DEBUG(10,("acl:moving as an NC\n"));
1167                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
1168         }
1169         /* new parent should have create child */
1170         talloc_free(tmp_ctx);
1171         tmp_ctx = talloc_new(req);
1172         root = NULL;
1173         new_node = NULL;
1174         guid = get_oc_guid_from_message(module, schema, acl_res->msgs[0]);
1175         if (!guid) {
1176                 DEBUG(10,("acl:renamed object has no object class\n"));
1177                 return ldb_module_done(req, NULL, NULL,  LDB_ERR_OPERATIONS_ERROR);
1178         }
1179
1180         ret = dsdb_module_check_access_on_dn(module, req, newparent, SEC_ADS_CREATE_CHILD, guid);
1181         if (ret != LDB_SUCCESS) {
1182                 DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn)));
1183                 return ret;
1184         }
1185         /* do we have delete object on the object? */
1186
1187         status = sec_access_check_ds(sd, acl_user_token(module),
1188                                      SEC_STD_DELETE,
1189                                      &access_granted,
1190                                      NULL,
1191                                      sid);
1192
1193         if (NT_STATUS_IS_OK(status)) {
1194                 return ldb_next_request(module, req);
1195         }
1196         /* what about delete child on the current parent */
1197         ret = dsdb_module_check_access_on_dn(module, req, oldparent, SEC_ADS_DELETE_CHILD, NULL);
1198         if (ret != LDB_SUCCESS) {
1199                 DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn)));
1200                 return ldb_module_done(req, NULL, NULL, ret);
1201         }
1202         return ldb_next_request(module, req);
1203 }
1204
1205 static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares)
1206 {
1207         struct ldb_context *ldb;
1208         struct acl_context *ac;
1209         struct acl_private *data;
1210         struct ldb_result *acl_res;
1211         static const char *acl_attrs[] = {
1212                 "objectClass",
1213                 "nTSecurityDescriptor",
1214                 "objectSid",
1215                 NULL
1216         };
1217         int ret, i;
1218
1219         ac = talloc_get_type(req->context, struct acl_context);
1220         data = talloc_get_type(ldb_module_get_private(ac->module), struct acl_private);
1221         ldb = ldb_module_get_ctx(ac->module);
1222
1223         if (!ares) {
1224                 return ldb_module_done(ac->req, NULL, NULL,
1225                                        LDB_ERR_OPERATIONS_ERROR);
1226         }
1227         if (ares->error != LDB_SUCCESS) {
1228                 return ldb_module_done(ac->req, ares->controls,
1229                                        ares->response, ares->error);
1230         }
1231
1232         switch (ares->type) {
1233         case LDB_REPLY_ENTRY:
1234                 if (ac->allowedAttributes 
1235                     || ac->allowedChildClasses
1236                     || ac->allowedChildClassesEffective
1237                     || ac->allowedAttributesEffective
1238                     || ac->sDRightsEffective) {
1239                         ret = dsdb_module_search_dn(ac->module, ac, &acl_res, ares->message->dn, 
1240                                                     acl_attrs,
1241                                                     DSDB_FLAG_NEXT_MODULE);
1242                         if (ret != LDB_SUCCESS) {
1243                                 return ldb_module_done(ac->req, NULL, NULL, ret);
1244                         }
1245                         if (ac->allowedAttributes || ac->allowedAttributesEffective) {
1246                                 ret = acl_allowedAttributes(ac->module, ac->schema, acl_res->msgs[0], ares->message, ac);
1247                                 if (ret != LDB_SUCCESS) {
1248                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1249                                 }
1250                         }
1251                         if (ac->allowedChildClasses) {
1252                                 ret = acl_childClasses(ac->module, ac->schema, acl_res->msgs[0],
1253                                                        ares->message, "allowedChildClasses");
1254                                 if (ret != LDB_SUCCESS) {
1255                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1256                                 }
1257                         }
1258                         if (ac->allowedChildClassesEffective) {
1259                                 ret = acl_childClassesEffective(ac->module, ac->schema,
1260                                                                 acl_res->msgs[0], ares->message, ac);
1261                                 if (ret != LDB_SUCCESS) {
1262                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1263                                 }
1264                         }
1265                         if (ac->sDRightsEffective) {
1266                                 ret = acl_sDRightsEffective(ac->module, 
1267                                                             acl_res->msgs[0], ares->message, ac);
1268                                 if (ret != LDB_SUCCESS) {
1269                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1270                                 }
1271                         }
1272                 }
1273                 if (data && data->password_attrs) {
1274                         if (!ac->am_system) {
1275                                 for (i = 0; data->password_attrs[i]; i++) {
1276                                         ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
1277                                 }
1278                         }
1279                 }
1280                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
1281
1282         case LDB_REPLY_REFERRAL:
1283                 return ldb_module_send_referral(ac->req, ares->referral);
1284
1285         case LDB_REPLY_DONE:
1286                 return ldb_module_done(ac->req, ares->controls,
1287                                        ares->response, LDB_SUCCESS);
1288
1289         }
1290         return LDB_SUCCESS;
1291 }
1292
1293 static int acl_search(struct ldb_module *module, struct ldb_request *req)
1294 {
1295         struct ldb_context *ldb;
1296         struct acl_context *ac;
1297         struct ldb_request *down_req;
1298         struct acl_private *data;
1299         int ret, i;
1300
1301         ldb = ldb_module_get_ctx(module);
1302
1303         ac = talloc_zero(req, struct acl_context);
1304         if (ac == NULL) {
1305                 return ldb_oom(ldb);
1306         }
1307         data = talloc_get_type(ldb_module_get_private(module), struct acl_private);
1308
1309         ac->module = module;
1310         ac->req = req;
1311         ac->am_system = dsdb_module_am_system(module);
1312         ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
1313         ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
1314         ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses");
1315         ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
1316         ac->sDRightsEffective = ldb_attr_in_list(req->op.search.attrs, "sDRightsEffective");
1317         ac->schema = dsdb_get_schema(ldb, ac);
1318
1319         /* replace any attributes in the parse tree that are private,
1320            so we don't allow a search for 'userPassword=penguin',
1321            just as we would not allow that attribute to be returned */
1322         if (ac->am_system) {
1323                 /* FIXME: We should copy the tree and keep the original unmodified. */
1324                 /* remove password attributes */
1325                 if (data && data->password_attrs) {
1326                         for (i = 0; data->password_attrs[i]; i++) {
1327                                 ldb_parse_tree_attr_replace(req->op.search.tree,
1328                                                             data->password_attrs[i],
1329                                                             "kludgeACLredactedattribute");
1330                         }
1331                 }
1332         }
1333         ret = ldb_build_search_req_ex(&down_req,
1334                                       ldb, ac,
1335                                       req->op.search.base,
1336                                       req->op.search.scope,
1337                                       req->op.search.tree,
1338                                       req->op.search.attrs,
1339                                       req->controls,
1340                                       ac, acl_search_callback,
1341                                       req);
1342         if (ret != LDB_SUCCESS) {
1343                 return ret;
1344         }
1345         /* perform the search */
1346         return ldb_next_request(module, down_req);
1347 }
1348
1349 _PUBLIC_ const struct ldb_module_ops ldb_acl_module_ops = {
1350         .name              = "acl",
1351         .search            = acl_search,
1352         .add               = acl_add,
1353         .modify            = acl_modify,
1354         .del               = acl_delete,
1355         .rename            = acl_rename,
1356         .init_context      = acl_module_init
1357 };