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