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