Split the dsdb_access_check_on_dn.
[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 /* performs an access check from inside the module stack
86  * given the dn of the object to be checked, the required access
87  * guid is either the guid of the extended right, or NULL
88  */
89
90 int dsdb_module_check_access_on_dn(struct ldb_module *module,
91                                    TALLOC_CTX *mem_ctx,
92                                    struct ldb_dn *dn,
93                                    uint32_t access,
94                                    const struct GUID *guid)
95 {
96         int ret;
97         struct ldb_result *acl_res;
98         static const char *acl_attrs[] = {
99                 "nTSecurityDescriptor",
100                 "objectSid",
101                 NULL
102         };
103         struct ldb_context *ldb = ldb_module_get_ctx(module);
104         struct auth_session_info *session_info
105                 = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
106         if(!session_info) {
107                 return LDB_ERR_OPERATIONS_ERROR;
108         }
109         ret = dsdb_module_search_dn(module, mem_ctx, &acl_res, dn,
110                                     acl_attrs, DSDB_SEARCH_SHOW_DELETED);
111         if (ret != LDB_SUCCESS) {
112                 DEBUG(10,("access_check: failed to find object %s\n", ldb_dn_get_linearized(dn)));
113                 return ret;
114         }
115         return dsdb_check_access_on_dn_internal(acl_res,
116                                                 mem_ctx,
117                                                 session_info->security_token,
118                                                 dn,
119                                                 access,
120                                                 guid);
121 }
122
123
124 static int acl_module_init(struct ldb_module *module)
125 {
126         struct ldb_context *ldb;
127         struct acl_private *data;
128         int ret, i;
129         TALLOC_CTX *mem_ctx = talloc_new(module);
130         static const char *attrs[] = { "passwordAttribute", NULL };
131         struct ldb_result *res;
132         struct ldb_message *msg;
133         struct ldb_message_element *password_attributes;
134
135         ldb = ldb_module_get_ctx(module);
136
137         ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
138         if (ret != LDB_SUCCESS) {
139                 ldb_debug(ldb, LDB_DEBUG_ERROR,
140                           "acl_module_init: Unable to register control with rootdse!\n");
141                 return LDB_ERR_OPERATIONS_ERROR;
142         }
143
144         data = talloc(module, struct acl_private);
145         if (data == NULL) {
146                 ldb_oom(ldb);
147                 return LDB_ERR_OPERATIONS_ERROR;
148         }
149
150         data->password_attrs = NULL;
151         data->acl_perform = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"),
152                                          NULL, "acl", "perform", false);
153         ldb_module_set_private(module, data);
154
155         if (!mem_ctx) {
156                 ldb_oom(ldb);
157                 return LDB_ERR_OPERATIONS_ERROR;
158         }
159
160         ret = dsdb_module_search_dn(module, mem_ctx, &res,
161                                     ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"),
162                                     attrs, 0);
163         if (ret != LDB_SUCCESS) {
164                 goto done;
165         }
166         if (res->count == 0) {
167                 goto done;
168         }
169
170         if (res->count > 1) {
171                 talloc_free(mem_ctx);
172                 return LDB_ERR_CONSTRAINT_VIOLATION;
173         }
174
175         msg = res->msgs[0];
176
177         password_attributes = ldb_msg_find_element(msg, "passwordAttribute");
178         if (!password_attributes) {
179                 goto done;
180         }
181         data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1);
182         if (!data->password_attrs) {
183                 talloc_free(mem_ctx);
184                 ldb_oom(ldb);
185                 return LDB_ERR_OPERATIONS_ERROR;
186         }
187         for (i=0; i < password_attributes->num_values; i++) {
188                 data->password_attrs[i] = (const char *)password_attributes->values[i].data;
189                 talloc_steal(data->password_attrs, password_attributes->values[i].data);
190         }
191         data->password_attrs[i] = NULL;
192
193 done:
194         talloc_free(mem_ctx);
195         return ldb_next_init(module);
196 }
197
198 static const struct GUID *get_oc_guid_from_message(struct ldb_module *module,
199                                                    struct ldb_message *msg)
200 {
201         struct ldb_message_element *oc_el;
202         struct ldb_context *ldb = ldb_module_get_ctx(module);
203
204         oc_el = ldb_msg_find_element(msg, "objectClass");
205         if (!oc_el) {
206                 return NULL;
207         }
208
209         return class_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb),
210                                                       (char *)oc_el->values[oc_el->num_values-1].data);
211 }
212
213 static int acl_check_access_on_attribute(struct ldb_module *module,
214                                          TALLOC_CTX *mem_ctx,
215                                          struct security_descriptor *sd,
216                                          struct dom_sid *rp_sid,
217                                          uint32_t access,
218                                          const struct dsdb_attribute *attr)
219 {
220         int ret;
221         NTSTATUS status;
222         uint32_t access_granted;
223         struct object_tree *root = NULL;
224         struct object_tree *new_node = NULL;
225         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
226         struct security_token *token = acl_user_token(module);
227         if (attr) {
228                 if (!GUID_all_zero(&attr->attributeSecurityGUID)) {
229                         if (!insert_in_object_tree(tmp_ctx,
230                                                    &attr->attributeSecurityGUID, access,
231                                                    &root, &new_node)) {
232                                 DEBUG(10, ("acl_search: cannot add to object tree securityGUID\n"));
233                                 goto fail;
234                         }
235
236                         if (!insert_in_object_tree(tmp_ctx,
237                                                    &attr->schemaIDGUID, access, &new_node, &new_node)) {
238                                 DEBUG(10, ("acl_search: cannot add to object tree attributeGUID\n"));
239                                 goto fail;
240                         }
241                 }
242                 else {
243                         if (!insert_in_object_tree(tmp_ctx,
244                                                    &attr->schemaIDGUID, access, &root, &new_node)) {
245                                 DEBUG(10, ("acl_search: cannot add to object tree attributeGUID\n"));
246                                 goto fail;
247                         }
248                 }
249         }
250         status = sec_access_check_ds(sd, token,
251                                      access,
252                                      &access_granted,
253                                      root,
254                                      rp_sid);
255         if (!NT_STATUS_IS_OK(status)) {
256                 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
257         }
258         else {
259                 ret = LDB_SUCCESS;
260         }
261         return ret;
262 fail:
263         return LDB_ERR_OPERATIONS_ERROR;
264 }
265
266 static int acl_check_access_on_class(struct ldb_module *module,
267                                      TALLOC_CTX *mem_ctx,
268                                      struct security_descriptor *sd,
269                                      struct dom_sid *rp_sid,
270                                      uint32_t access,
271                                      const char *class_name)
272 {
273         int ret;
274         struct ldb_context *ldb = ldb_module_get_ctx(module);
275         NTSTATUS status;
276         uint32_t access_granted;
277         struct object_tree *root = NULL;
278         struct object_tree *new_node = NULL;
279         const struct GUID *guid;
280         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
281         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
282         struct security_token *token = acl_user_token(module);
283         if (class_name) {
284                 guid = class_schemaid_guid_by_lDAPDisplayName(schema, class_name);
285                 if (!guid) {
286                         DEBUG(10, ("acl_search: cannot find class %s\n",
287                                    class_name));
288                         goto fail;
289                 }
290                 if (!insert_in_object_tree(tmp_ctx,
291                                            guid, access,
292                                            &root, &new_node)) {
293                         DEBUG(10, ("acl_search: cannot add to object tree guid\n"));
294                         goto fail;
295                 }
296         }
297         status = sec_access_check_ds(sd, token,
298                                      access,
299                                      &access_granted,
300                                      root,
301                                      rp_sid);
302         if (!NT_STATUS_IS_OK(status)) {
303                 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
304         }
305         else {
306                 ret = LDB_SUCCESS;
307         }
308         return ret;
309 fail:
310         return LDB_ERR_OPERATIONS_ERROR;
311 }
312
313 static int acl_allowedAttributes(struct ldb_module *module,
314                                  struct ldb_message *sd_msg,
315                                  struct ldb_message *msg,
316                                  struct acl_context *ac)
317 {
318         struct ldb_message_element *oc_el;
319         struct ldb_context *ldb = ldb_module_get_ctx(module);
320         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
321         TALLOC_CTX *mem_ctx;
322         const char **attr_list;
323         int i, ret;
324
325         /* If we don't have a schema yet, we can't do anything... */
326         if (schema == NULL) {
327                 return LDB_SUCCESS;
328         }
329
330         /* Must remove any existing attribute */
331         if (ac->allowedAttributes) {
332                 ldb_msg_remove_attr(msg, "allowedAttributes");
333         }
334
335         mem_ctx = talloc_new(msg);
336         if (!mem_ctx) {
337                 ldb_oom(ldb);
338                 return LDB_ERR_OPERATIONS_ERROR;
339         }
340
341         oc_el = ldb_msg_find_element(sd_msg, "objectClass");
342         attr_list = dsdb_full_attribute_list(mem_ctx, schema, oc_el, DSDB_SCHEMA_ALL);
343         if (!attr_list) {
344                 ldb_asprintf_errstring(ldb, "acl: Failed to get list of attributes");
345                 talloc_free(mem_ctx);
346                 return LDB_ERR_OPERATIONS_ERROR;
347         }
348         if (ac->allowedAttributes) {
349                 for (i=0; attr_list && attr_list[i]; i++) {
350                         ldb_msg_add_string(msg, "allowedAttributes", attr_list[i]);
351                 }
352         }
353         if (ac->allowedAttributesEffective) {
354                 struct security_descriptor *sd;
355                 struct dom_sid *sid = NULL;
356                 struct ldb_control *as_system = ldb_request_get_control(ac->req,
357                                                                         LDB_CONTROL_AS_SYSTEM_OID);
358
359                 if (as_system != NULL) {
360                         as_system->critical = 0;
361                 }
362
363                 ldb_msg_remove_attr(msg, "allowedAttributesEffective");
364                 if (ac->am_system || as_system) {
365                         for (i=0; attr_list && attr_list[i]; i++) {
366                                 ldb_msg_add_string(msg, "allowedAttributesEffective", attr_list[i]);
367                         }
368                         return LDB_SUCCESS;
369                 }
370
371                 ret = dsdb_get_sd_from_ldb_message(mem_ctx, sd_msg, &sd);
372
373                 if (ret != LDB_SUCCESS) {
374                         return ret;
375                 }
376                 ret = dsdb_get_dom_sid_from_ldb_message(mem_ctx, sd_msg, &sid);
377
378                 if (ret != LDB_SUCCESS) {
379                         return ret;
380                 }
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_ERR_OPERATIONS_ERROR;
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                             struct ldb_message *sd_msg,
409                             struct ldb_message *msg,
410                             const char *attrName)
411 {
412         struct ldb_message_element *oc_el;
413         struct ldb_message_element *allowedClasses;
414         struct ldb_context *ldb = ldb_module_get_ctx(module);
415         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
416         const struct dsdb_class *sclass;
417         int i, j, ret;
418
419         /* If we don't have a schema yet, we can't do anything... */
420         if (schema == NULL) {
421                 return LDB_SUCCESS;
422         }
423
424         /* Must remove any existing attribute, or else confusion reins */
425         ldb_msg_remove_attr(msg, attrName);
426         ret = ldb_msg_add_empty(msg, attrName, 0, &allowedClasses);
427         if (ret != LDB_SUCCESS) {
428                 return ret;
429         }
430
431         oc_el = ldb_msg_find_element(sd_msg, "objectClass");
432
433         for (i=0; oc_el && i < oc_el->num_values; i++) {
434                 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
435                 if (!sclass) {
436                         /* We don't know this class?  what is going on? */
437                         continue;
438                 }
439
440                 for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
441                         ldb_msg_add_string(msg, attrName, sclass->possibleInferiors[j]);
442                 }
443         }
444         if (allowedClasses->num_values > 1) {
445                 TYPESAFE_QSORT(allowedClasses->values, allowedClasses->num_values, data_blob_cmp);
446                 for (i=1 ; i < allowedClasses->num_values; i++) {
447                         struct ldb_val *val1 = &allowedClasses->values[i-1];
448                         struct ldb_val *val2 = &allowedClasses->values[i];
449                         if (data_blob_cmp(val1, val2) == 0) {
450                                 memmove(val1, val2, (allowedClasses->num_values - i) * sizeof(struct ldb_val));
451                                 allowedClasses->num_values--;
452                                 i--;
453                         }
454                 }
455         }
456
457         return LDB_SUCCESS;
458 }
459
460 static int acl_childClassesEffective(struct ldb_module *module,
461                                      struct ldb_message *sd_msg,
462                                      struct ldb_message *msg,
463                                      struct acl_context *ac)
464 {
465         struct ldb_message_element *oc_el;
466         struct ldb_message_element *allowedClasses = NULL;
467         struct ldb_context *ldb = ldb_module_get_ctx(module);
468         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
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         int i, j, ret;
475
476         if (as_system != NULL) {
477                 as_system->critical = 0;
478         }
479
480         if (ac->am_system || as_system) {
481                 return acl_childClasses(module, sd_msg, msg, "allowedChildClassesEffective");
482         }
483
484         /* If we don't have a schema yet, we can't do anything... */
485         if (schema == NULL) {
486                 return LDB_SUCCESS;
487         }
488
489         /* Must remove any existing attribute, or else confusion reins */
490         ldb_msg_remove_attr(msg, "allowedChildClassesEffective");
491
492         oc_el = ldb_msg_find_element(sd_msg, "objectClass");
493         ret = dsdb_get_sd_from_ldb_message(msg, sd_msg, &sd);
494         if (ret != LDB_SUCCESS) {
495                 return ret;
496         }
497         ret = dsdb_get_dom_sid_from_ldb_message(msg, sd_msg, &sid);
498
499         if (ret != LDB_SUCCESS) {
500                 return ret;
501         }
502         for (i=0; oc_el && i < oc_el->num_values; i++) {
503                 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
504                 if (!sclass) {
505                         /* We don't know this class?  what is going on? */
506                         continue;
507                 }
508
509                 for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
510                         ret = acl_check_access_on_class(module,
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(msg, sd_msg, &sd);
571                 if (ret != LDB_SUCCESS) {
572                         return ret;
573                 }
574                 ret = dsdb_get_dom_sid_from_ldb_message(msg, sd_msg, &sid);
575
576                 if (ret != LDB_SUCCESS) {
577                         return ret;
578                 }
579                 ret = acl_check_access_on_attribute(module,
580                                                     msg,
581                                                     sd,
582                                                     sid,
583                                                     SEC_STD_WRITE_OWNER,
584                                                     NULL);
585                 if (ret == LDB_SUCCESS) {
586                         flags |= SECINFO_OWNER | SECINFO_GROUP;
587                 }
588                 ret = acl_check_access_on_attribute(module,
589                                                     msg,
590                                                     sd,
591                                                     sid,
592                                                     SEC_STD_WRITE_DAC,
593                                                     NULL);
594                 if (ret == LDB_SUCCESS) {
595                         flags |= SECINFO_DACL;
596                 }
597                 ret = acl_check_access_on_attribute(module,
598                                                     msg,
599                                                     sd,
600                                                     sid,
601                                                     SEC_FLAG_SYSTEM_SECURITY,
602                                                     NULL);
603                 if (ret == LDB_SUCCESS) {
604                         flags |= SECINFO_SACL;
605                 }
606         }
607         ldb_msg_add_fmt(msg, "sDRightsEffective", "%u", flags);
608         return LDB_SUCCESS;
609 }
610
611 static int acl_add(struct ldb_module *module, struct ldb_request *req)
612 {
613         int ret;
614         struct ldb_dn *parent = ldb_dn_get_parent(req, req->op.add.message->dn);
615         struct ldb_context *ldb;
616         struct ldb_message_element *oc_el;
617         const struct GUID *guid;
618         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
619
620         if (as_system != NULL) {
621                 as_system->critical = 0;
622         }
623
624         if (dsdb_module_am_system(module) || as_system) {
625                 return ldb_next_request(module, req);
626         }
627
628         if (ldb_dn_is_special(req->op.add.message->dn)) {
629                 return ldb_next_request(module, req);
630         }
631         ldb = ldb_module_get_ctx(module);
632         /* Creating an NC. There is probably something we should do here,
633          * but we will establish that later */
634         if ((ldb_dn_compare(req->op.add.message->dn, (ldb_get_schema_basedn(ldb))) == 0) ||
635             (ldb_dn_compare(req->op.add.message->dn, (ldb_get_config_basedn(ldb))) == 0) ||
636             (ldb_dn_compare(req->op.add.message->dn, (ldb_get_root_basedn(ldb))) == 0)) {
637                 return ldb_next_request(module, req);
638         }
639
640         oc_el = ldb_msg_find_element(req->op.add.message, "objectClass");
641         if (!oc_el || oc_el->num_values == 0) {
642                 DEBUG(10,("acl:operation error %s\n", ldb_dn_get_linearized(req->op.add.message->dn)));
643                 return ldb_module_done(req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
644         }
645
646         guid = class_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb),
647                                                       (char *)oc_el->values[oc_el->num_values-1].data);
648         ret = dsdb_module_check_access_on_dn(module, req, parent, SEC_ADS_CREATE_CHILD, guid);
649         if (ret != LDB_SUCCESS) {
650                 return ret;
651         }
652         return ldb_next_request(module, req);
653 }
654
655 static int acl_modify(struct ldb_module *module, struct ldb_request *req)
656 {
657         int ret;
658         struct ldb_context *ldb = ldb_module_get_ctx(module);
659         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
660         unsigned int i;
661         bool modify_sd = false;
662         const struct GUID *guid;
663         uint32_t access_granted;
664         struct object_tree *root = NULL;
665         struct object_tree *new_node = NULL;
666         NTSTATUS status;
667         struct ldb_result *acl_res;
668         struct security_descriptor *sd;
669         struct dom_sid *sid = NULL;
670         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
671         TALLOC_CTX *tmp_ctx = talloc_new(req);
672         static const char *acl_attrs[] = {
673                 "nTSecurityDescriptor",
674                 "objectClass",
675                 "objectSid",
676                 NULL
677         };
678
679         if (as_system != NULL) {
680                 as_system->critical = 0;
681         }
682
683         /* Don't print this debug statement if elements[0].name is going to be NULL */
684         if(req->op.mod.message->num_elements > 0)
685         {
686                 DEBUG(10, ("ldb:acl_modify: %s\n", req->op.mod.message->elements[0].name));
687         }
688         if (dsdb_module_am_system(module) || as_system) {
689                 return ldb_next_request(module, req);
690         }
691         if (ldb_dn_is_special(req->op.mod.message->dn)) {
692                 return ldb_next_request(module, req);
693         }
694         ret = dsdb_module_search_dn(module, req, &acl_res, req->op.mod.message->dn,
695                                     acl_attrs, 0);
696
697         if (ret != LDB_SUCCESS) {
698                 return ret;
699         }
700
701         ret = dsdb_get_sd_from_ldb_message(req, acl_res->msgs[0], &sd);
702         if (ret != LDB_SUCCESS) {
703                 DEBUG(10, ("acl_modify: cannot get descriptor\n"));
704                 return ret;
705         }
706         /* Theoretically we pass the check if the object has no sd */
707         if (!sd) {
708                 return LDB_SUCCESS;
709         }
710
711         guid = get_oc_guid_from_message(module,acl_res->msgs[0]);
712         if (!guid) {
713                 DEBUG(10, ("acl_modify: cannot get guid\n"));
714                 goto fail;
715         }
716
717         ret = dsdb_get_dom_sid_from_ldb_message(req, acl_res->msgs[0], &sid);
718         if (ret != LDB_SUCCESS) {
719                 return LDB_ERR_OPERATIONS_ERROR;
720         }
721
722         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
723                                    &root, &new_node)) {
724                 DEBUG(10, ("acl_modify: cannot add to object tree\n"));
725                 goto fail;
726         }
727         for (i=0; i < req->op.mod.message->num_elements; i++){
728                 const struct dsdb_attribute *attr;
729                 /* clearTextPassword is not in schema */
730                 if (strcmp("clearTextPassword", req->op.mod.message->elements[i].name) == 0) {
731                         attr = dsdb_attribute_by_lDAPDisplayName(schema, "unicodePwd");
732                 } else {
733                         attr = dsdb_attribute_by_lDAPDisplayName(schema,
734                                                                  req->op.mod.message->elements[i].name);
735                 }
736                 if (strcmp("nTSecurityDescriptor", req->op.mod.message->elements[i].name) == 0) {
737                         modify_sd = true;
738                 } else {
739
740                         if (!attr) {
741                                 DEBUG(10, ("acl_modify: cannot find attribute %s\n",
742                                            req->op.mod.message->elements[i].name));
743                                 goto fail;
744                         }
745                         if (!insert_in_object_tree(tmp_ctx,
746                                                    &attr->attributeSecurityGUID, SEC_ADS_WRITE_PROP,
747                                                    &new_node, &new_node)) {
748                                 DEBUG(10, ("acl_modify: cannot add to object tree securityGUID\n"));
749                                 goto fail;
750                         }
751
752                         if (!insert_in_object_tree(tmp_ctx,
753                                                    &attr->schemaIDGUID, SEC_ADS_WRITE_PROP, &new_node, &new_node)) {
754                                 DEBUG(10, ("acl_modify: cannot add to object tree attributeGUID\n"));
755                                 goto fail;
756                         }
757                 }
758         }
759
760         if (root->num_of_children > 0) {
761                 status = sec_access_check_ds(sd, acl_user_token(module),
762                                              SEC_ADS_WRITE_PROP,
763                                              &access_granted,
764                                              root,
765                                              sid);
766
767                 if (!NT_STATUS_IS_OK(status)) {
768                         DEBUG(10, ("Object %s nas no write property access\n",
769                                    ldb_dn_get_linearized(req->op.mod.message->dn)));
770                         dsdb_acl_debug(sd,
771                                   acl_user_token(module),
772                                   req->op.mod.message->dn,
773                                   true,
774                                   10);
775                         talloc_free(tmp_ctx);
776                         return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
777                 }
778         }
779         if (modify_sd) {
780                 status = sec_access_check_ds(sd, acl_user_token(module),
781                                              SEC_STD_WRITE_DAC,
782                                              &access_granted,
783                                              NULL,
784                                              sid);
785
786                 if (!NT_STATUS_IS_OK(status)) {
787                         DEBUG(10, ("Object %s nas no write dacl access\n",
788                                    ldb_dn_get_linearized(req->op.mod.message->dn)));
789                         dsdb_acl_debug(sd,
790                                   acl_user_token(module),
791                                   req->op.mod.message->dn,
792                                   true,
793                                   10);
794                         talloc_free(tmp_ctx);
795                         return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
796                 }
797         }
798
799         talloc_free(tmp_ctx);
800         return ldb_next_request(module, req);
801 fail:
802         talloc_free(tmp_ctx);
803         return LDB_ERR_OPERATIONS_ERROR;
804 }
805
806 /* similar to the modify for the time being.
807  * We need to concider the special delete tree case, though - TODO */
808 static int acl_delete(struct ldb_module *module, struct ldb_request *req)
809 {
810         int ret;
811         struct ldb_dn *parent = ldb_dn_get_parent(req, req->op.del.dn);
812         struct ldb_context *ldb;
813         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
814
815         if (as_system != NULL) {
816                 as_system->critical = 0;
817         }
818
819         DEBUG(10, ("ldb:acl_delete: %s\n", ldb_dn_get_linearized(req->op.del.dn)));
820         if (dsdb_module_am_system(module) || as_system) {
821                 return ldb_next_request(module, req);
822         }
823
824         if (ldb_dn_is_special(req->op.del.dn)) {
825                 return ldb_next_request(module, req);
826         }
827         ldb = ldb_module_get_ctx(module);
828         /* first check if we have delete object right */
829         ret = dsdb_module_check_access_on_dn(module, req, req->op.del.dn, SEC_STD_DELETE, NULL);
830         if (ret == LDB_SUCCESS) {
831                 return ldb_next_request(module, req);
832         }
833
834         /* Nope, we don't have delete object. Lets check if we have delete child on the parent */
835         /* No parent, so check fails */
836         if ((ldb_dn_compare(req->op.del.dn, (ldb_get_schema_basedn(ldb))) == 0) ||
837             (ldb_dn_compare(req->op.del.dn, (ldb_get_config_basedn(ldb))) == 0) ||
838             (ldb_dn_compare(req->op.del.dn, (ldb_get_root_basedn(ldb))) == 0)) {
839                 DEBUG(10,("acl:deleting an NC\n"));
840                 return ldb_module_done(req, NULL, NULL, LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS);
841         }
842
843         ret = dsdb_module_check_access_on_dn(module, req, parent, SEC_ADS_DELETE_CHILD, NULL);
844         if (ret != LDB_SUCCESS) {
845                 return ret;
846         }
847         return ldb_next_request(module, req);
848 }
849
850 static int acl_rename(struct ldb_module *module, struct ldb_request *req)
851 {
852         int ret;
853         struct ldb_dn *oldparent = ldb_dn_get_parent(req, req->op.rename.olddn);
854         struct ldb_dn *newparent = ldb_dn_get_parent(req, req->op.rename.newdn);
855         struct ldb_context *ldb;
856         struct security_descriptor *sd = NULL;
857         struct dom_sid *sid = NULL;
858         struct ldb_result *acl_res;
859         const struct GUID *guid;
860         struct object_tree *root = NULL;
861         struct object_tree *new_node = NULL;
862         struct ldb_control *as_system = ldb_request_get_control(req, LDB_CONTROL_AS_SYSTEM_OID);
863         TALLOC_CTX *tmp_ctx = talloc_new(req);
864         NTSTATUS status;
865         uint32_t access_granted;
866         const char *rdn_name;
867         static const char *acl_attrs[] = {
868                 "nTSecurityDescriptor",
869                 "objectClass",
870                 "objectSid",
871                 NULL
872         };
873
874         if (as_system != NULL) {
875                 as_system->critical = 0;
876         }
877
878         DEBUG(10, ("ldb:acl_rename: %s\n", ldb_dn_get_linearized(req->op.rename.olddn)));
879         if (dsdb_module_am_system(module) || as_system) {
880                 return ldb_next_request(module, req);
881         }
882         if (ldb_dn_is_special(req->op.rename.olddn)) {
883                 return ldb_next_request(module, req);
884         }
885         ldb = ldb_module_get_ctx(module);
886
887         ret = dsdb_module_search_dn(module, req, &acl_res, req->op.rename.olddn,
888                                     acl_attrs, DSDB_SEARCH_SHOW_DELETED);
889         /* we sould be able to find the parent */
890         if (ret != LDB_SUCCESS) {
891                 DEBUG(10,("acl: failed to find object %s\n",
892                           ldb_dn_get_linearized(req->op.rename.olddn)));
893                 return ret;
894         }
895
896         guid = get_oc_guid_from_message(module,acl_res->msgs[0]);
897         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
898                                    &root, &new_node)) {
899                 return LDB_ERR_OPERATIONS_ERROR;
900         };
901
902         guid = attribute_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb),
903                                                           "name");
904         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
905                                    &new_node, &new_node)) {
906                 return LDB_ERR_OPERATIONS_ERROR;
907         };
908
909         rdn_name = ldb_dn_get_rdn_name(req->op.rename.olddn);
910         if (rdn_name == NULL) {
911                 return LDB_ERR_OPERATIONS_ERROR;
912         }
913         guid = attribute_schemaid_guid_by_lDAPDisplayName(dsdb_get_schema(ldb),
914                                                           rdn_name);
915         if (!insert_in_object_tree(tmp_ctx, guid, SEC_ADS_WRITE_PROP,
916                                    &new_node, &new_node)) {
917                 return LDB_ERR_OPERATIONS_ERROR;
918         };
919
920         ret = dsdb_get_sd_from_ldb_message(req, acl_res->msgs[0], &sd);
921
922         if (ret != LDB_SUCCESS) {
923                 return LDB_ERR_OPERATIONS_ERROR;
924         }
925         /* Theoretically we pass the check if the object has no sd */
926         if (!sd) {
927                 return LDB_SUCCESS;
928         }
929         ret = dsdb_get_dom_sid_from_ldb_message(req, acl_res->msgs[0], &sid);
930         if (ret != LDB_SUCCESS) {
931                 return LDB_ERR_OPERATIONS_ERROR;
932         }
933
934         status = sec_access_check_ds(sd, acl_user_token(module),
935                                      SEC_ADS_WRITE_PROP,
936                                      &access_granted,
937                                      root,
938                                      sid);
939
940         if (!NT_STATUS_IS_OK(status)) {
941                 DEBUG(10, ("Object %s nas no wp on name\n",
942                            ldb_dn_get_linearized(req->op.rename.olddn)));
943                 dsdb_acl_debug(sd,
944                           acl_user_token(module),
945                           req->op.rename.olddn,
946                           true,
947                           10);
948                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
949         }
950
951         if (ldb_dn_compare(oldparent, newparent) == 0) {
952                 /* regular rename, not move, nothing more to do */
953                 return ldb_next_request(module, req);
954         }
955
956         /* What exactly to do in this case? It would fail anyway.. */
957         if ((ldb_dn_compare(req->op.rename.newdn, (ldb_get_schema_basedn(ldb))) == 0) ||
958             (ldb_dn_compare(req->op.rename.newdn, (ldb_get_config_basedn(ldb))) == 0) ||
959             (ldb_dn_compare(req->op.rename.newdn, (ldb_get_root_basedn(ldb))) == 0)) {
960                 DEBUG(10,("acl:moving as an NC\n"));
961                 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
962         }
963         /* new parent should have create child */
964         talloc_free(tmp_ctx);
965         tmp_ctx = talloc_new(req);
966         root = NULL;
967         new_node = NULL;
968         guid = get_oc_guid_from_message(module,acl_res->msgs[0]);
969         if (!guid) {
970                 DEBUG(10,("acl:renamed object has no object class\n"));
971                 return ldb_module_done(req, NULL, NULL,  LDB_ERR_OPERATIONS_ERROR);
972         }
973
974         ret = dsdb_module_check_access_on_dn(module, req, newparent, SEC_ADS_CREATE_CHILD, guid);
975         if (ret != LDB_SUCCESS) {
976                 DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn)));
977                 return ret;
978         }
979         /* do we have delete object on the object? */
980
981         status = sec_access_check_ds(sd, acl_user_token(module),
982                                      SEC_STD_DELETE,
983                                      &access_granted,
984                                      NULL,
985                                      sid);
986
987         if (NT_STATUS_IS_OK(status)) {
988                 return ldb_next_request(module, req);
989         }
990         /* what about delete child on the current parent */
991         ret = dsdb_module_check_access_on_dn(module, req, oldparent, SEC_ADS_DELETE_CHILD, NULL);
992         if (ret != LDB_SUCCESS) {
993                 DEBUG(10,("acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn)));
994                 return ldb_module_done(req, NULL, NULL, ret);
995         }
996         return ldb_next_request(module, req);
997 }
998
999 static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares)
1000 {
1001         struct ldb_context *ldb;
1002         struct acl_context *ac;
1003         struct acl_private *data;
1004         struct ldb_result *acl_res;
1005         static const char *acl_attrs[] = {
1006                 "objectClass",
1007                 "nTSecurityDescriptor",
1008                 "objectSid",
1009                 NULL
1010         };
1011         int ret, i;
1012
1013         ac = talloc_get_type(req->context, struct acl_context);
1014         data = talloc_get_type(ldb_module_get_private(ac->module), struct acl_private);
1015         ldb = ldb_module_get_ctx(ac->module);
1016
1017         if (!ares) {
1018                 return ldb_module_done(ac->req, NULL, NULL,
1019                                        LDB_ERR_OPERATIONS_ERROR);
1020         }
1021         if (ares->error != LDB_SUCCESS) {
1022                 return ldb_module_done(ac->req, ares->controls,
1023                                        ares->response, ares->error);
1024         }
1025
1026         switch (ares->type) {
1027         case LDB_REPLY_ENTRY:
1028                 if (ac->allowedAttributes 
1029                     || ac->allowedChildClasses
1030                     || ac->allowedChildClassesEffective
1031                     || ac->allowedAttributesEffective
1032                     || ac->sDRightsEffective) {
1033                         ret = dsdb_module_search_dn(ac->module, ac, &acl_res, ares->message->dn, 
1034                                                     acl_attrs, 0);
1035                         if (ret != LDB_SUCCESS) {
1036                                 return ldb_module_done(ac->req, NULL, NULL, ret);
1037                         }
1038                         if (ac->allowedAttributes || ac->allowedAttributesEffective) {
1039                                 ret = acl_allowedAttributes(ac->module, acl_res->msgs[0], ares->message, ac);
1040                                 if (ret != LDB_SUCCESS) {
1041                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1042                                 }
1043                         }
1044                         if (ac->allowedChildClasses) {
1045                                 ret = acl_childClasses(ac->module, acl_res->msgs[0],
1046                                                        ares->message, "allowedChildClasses");
1047                                 if (ret != LDB_SUCCESS) {
1048                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1049                                 }
1050                         }
1051                         if (ac->allowedChildClassesEffective) {
1052                                 ret = acl_childClassesEffective(ac->module,
1053                                                                 acl_res->msgs[0], ares->message, ac);
1054                                 if (ret != LDB_SUCCESS) {
1055                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1056                                 }
1057                         }
1058                         if (ac->sDRightsEffective) {
1059                                 ret = acl_sDRightsEffective(ac->module,
1060                                                             acl_res->msgs[0], ares->message, ac);
1061                                 if (ret != LDB_SUCCESS) {
1062                                         return ldb_module_done(ac->req, NULL, NULL, ret);
1063                                 }
1064                         }
1065                 }
1066                 if (data && data->password_attrs) {
1067                         if (!ac->am_system) {
1068                                 for (i = 0; data->password_attrs[i]; i++) {
1069                                         ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
1070                                 }
1071                         }
1072                 }
1073                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
1074
1075         case LDB_REPLY_REFERRAL:
1076                 return ldb_module_send_referral(ac->req, ares->referral);
1077
1078         case LDB_REPLY_DONE:
1079                 return ldb_module_done(ac->req, ares->controls,
1080                                        ares->response, LDB_SUCCESS);
1081
1082         }
1083         return LDB_SUCCESS;
1084 }
1085
1086 static int acl_search(struct ldb_module *module, struct ldb_request *req)
1087 {
1088         struct ldb_context *ldb;
1089         struct acl_context *ac;
1090         struct ldb_request *down_req;
1091         struct acl_private *data;
1092         int ret, i;
1093
1094         ldb = ldb_module_get_ctx(module);
1095
1096         ac = talloc_zero(req, struct acl_context);
1097         if (ac == NULL) {
1098                 ldb_oom(ldb);
1099                 return LDB_ERR_OPERATIONS_ERROR;
1100         }
1101         data = talloc_get_type(ldb_module_get_private(module), struct acl_private);
1102
1103         ac->module = module;
1104         ac->req = req;
1105         ac->am_system = dsdb_module_am_system(module);
1106         ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
1107         ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
1108         ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses");
1109         ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
1110         ac->sDRightsEffective = ldb_attr_in_list(req->op.search.attrs, "sDRightsEffective");
1111
1112         /* replace any attributes in the parse tree that are private,
1113            so we don't allow a search for 'userPassword=penguin',
1114            just as we would not allow that attribute to be returned */
1115         if (ac->am_system) {
1116                 /* FIXME: We should copy the tree and keep the original unmodified. */
1117                 /* remove password attributes */
1118                 if (data && data->password_attrs) {
1119                         for (i = 0; data->password_attrs[i]; i++) {
1120                                 ldb_parse_tree_attr_replace(req->op.search.tree,
1121                                                             data->password_attrs[i],
1122                                                             "kludgeACLredactedattribute");
1123                         }
1124                 }
1125         }
1126         ret = ldb_build_search_req_ex(&down_req,
1127                                       ldb, ac,
1128                                       req->op.search.base,
1129                                       req->op.search.scope,
1130                                       req->op.search.tree,
1131                                       req->op.search.attrs,
1132                                       req->controls,
1133                                       ac, acl_search_callback,
1134                                       req);
1135         if (ret != LDB_SUCCESS) {
1136                 return ret;
1137         }
1138         /* perform the search */
1139         return ldb_next_request(module, down_req);
1140 }
1141
1142 _PUBLIC_ const struct ldb_module_ops ldb_acl_module_ops = {
1143         .name              = "acl",
1144         .search            = acl_search,
1145         .add               = acl_add,
1146         .modify            = acl_modify,
1147         .del               = acl_delete,
1148         .rename            = acl_rename,
1149         .init_context      = acl_module_init
1150 };