Cosmetic corrections for the DSDB module
[Samba/fernandojvsilva.git] / source4 / dsdb / samdb / ldb_modules / kludge_acl.c
blob865e1c7286823bdbd61c5607a97f9c26dc426151
1 /*
2 ldb database library
4 Copyright (C) Andrew Bartlett 2005
5 Copyright (C) Simo Sorce 2006-2008
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 * Name: ldb
24 * Component: ldb kludge ACL module
26 * Description: Simple module to enforce a simple form of access
27 * control, sufficient for securing a default Samba4
28 * installation.
30 * Author: Andrew Bartlett
33 #include "includes.h"
34 #include "ldb/include/ldb.h"
35 #include "ldb/include/ldb_errors.h"
36 #include "ldb/include/ldb_private.h"
37 #include "auth/auth.h"
38 #include "libcli/security/security.h"
39 #include "dsdb/samdb/samdb.h"
41 /* Kludge ACL rules:
43 * - System can read passwords
44 * - Administrators can write anything
45 * - Users can read anything that is not a password
49 struct kludge_private_data {
50 const char **password_attrs;
53 static enum security_user_level what_is_user(struct ldb_module *module)
55 struct auth_session_info *session_info
56 = (struct auth_session_info *)ldb_get_opaque(module->ldb, "sessionInfo");
57 return security_session_user_level(session_info);
60 static const char *user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module)
62 struct auth_session_info *session_info
63 = (struct auth_session_info *)ldb_get_opaque(module->ldb, "sessionInfo");
64 if (!session_info) {
65 return "UNKNOWN (NULL)";
68 return talloc_asprintf(mem_ctx, "%s\\%s",
69 session_info->server_info->domain_name,
70 session_info->server_info->account_name);
73 /* search */
74 struct kludge_acl_context {
76 struct ldb_module *module;
77 struct ldb_request *req;
79 enum security_user_level user_type;
80 bool allowedAttributes;
81 bool allowedAttributesEffective;
82 bool allowedChildClasses;
83 bool allowedChildClassesEffective;
84 const char * const *attrs;
87 /* read all objectClasses */
89 static int kludge_acl_allowedAttributes(struct ldb_context *ldb, struct ldb_message *msg,
90 const char *attrName)
92 struct ldb_message_element *oc_el;
93 struct ldb_message_element *allowedAttributes;
94 const struct dsdb_schema *schema = dsdb_get_schema(ldb);
95 TALLOC_CTX *mem_ctx;
96 char **objectclass_list, **attr_list;
97 int i, ret;
99 /* If we don't have a schema yet, we can't do anything... */
100 if (schema == NULL) {
101 return LDB_SUCCESS;
104 /* Must remove any existing attribute, or else confusion reins */
105 ldb_msg_remove_attr(msg, attrName);
106 ret = ldb_msg_add_empty(msg, attrName, 0, &allowedAttributes);
107 if (ret != LDB_SUCCESS) {
108 return ret;
111 mem_ctx = talloc_new(msg);
112 if (!mem_ctx) {
113 ldb_oom(ldb);
114 return LDB_ERR_OPERATIONS_ERROR;
117 /* To ensure that oc_el is valid, we must look for it after
118 we alter the element array in ldb_msg_add_empty() */
119 oc_el = ldb_msg_find_element(msg, "objectClass");
121 objectclass_list = talloc_array(mem_ctx, char *, oc_el->num_values + 1);
122 if (!objectclass_list) {
123 ldb_oom(ldb);
124 talloc_free(mem_ctx);
125 return LDB_ERR_OPERATIONS_ERROR;
128 for (i=0; oc_el && i < oc_el->num_values; i++) {
129 objectclass_list[i] = (char *)oc_el->values[i].data;
131 objectclass_list[i] = NULL;
133 attr_list = dsdb_full_attribute_list(mem_ctx, schema, (const char **)objectclass_list, DSDB_SCHEMA_ALL);
134 if (!attr_list) {
135 ldb_asprintf_errstring(ldb, "kludge_acl: Failed to get list of attributes create %s attribute", attrName);
136 talloc_free(mem_ctx);
137 return LDB_ERR_OPERATIONS_ERROR;
140 for (i=0; attr_list && attr_list[i]; i++) {
141 ldb_msg_add_string(msg, attrName, attr_list[i]);
143 talloc_free(mem_ctx);
144 return LDB_SUCCESS;
147 /* read all objectClasses */
149 static int kludge_acl_childClasses(struct ldb_context *ldb, struct ldb_message *msg,
150 const char *attrName)
152 struct ldb_message_element *oc_el;
153 struct ldb_message_element *allowedClasses;
154 const struct dsdb_schema *schema = dsdb_get_schema(ldb);
155 const struct dsdb_class *class;
156 int i, j, ret;
158 /* If we don't have a schema yet, we can't do anything... */
159 if (schema == NULL) {
160 return LDB_SUCCESS;
163 /* Must remove any existing attribute, or else confusion reins */
164 ldb_msg_remove_attr(msg, attrName);
165 ret = ldb_msg_add_empty(msg, attrName, 0, &allowedClasses);
166 if (ret != LDB_SUCCESS) {
167 return ret;
170 /* To ensure that oc_el is valid, we must look for it after
171 we alter the element array in ldb_msg_add_empty() */
172 oc_el = ldb_msg_find_element(msg, "objectClass");
174 for (i=0; oc_el && i < oc_el->num_values; i++) {
175 class = dsdb_class_by_lDAPDisplayName(schema, (const char *)oc_el->values[i].data);
176 if (!class) {
177 /* We don't know this class? what is going on? */
178 continue;
181 for (j=0; class->possibleInferiors && class->possibleInferiors[j]; j++) {
182 ldb_msg_add_string(msg, attrName, class->possibleInferiors[j]);
186 if (allowedClasses->num_values > 1) {
187 qsort(allowedClasses->values,
188 allowedClasses->num_values,
189 sizeof(*allowedClasses->values),
190 (comparison_fn_t)data_blob_cmp);
192 for (i=1 ; i < allowedClasses->num_values; i++) {
194 struct ldb_val *val1 = &allowedClasses->values[i-1];
195 struct ldb_val *val2 = &allowedClasses->values[i];
196 if (data_blob_cmp(val1, val2) == 0) {
197 memmove(val1, val2, (allowedClasses->num_values - i) * sizeof( struct ldb_val));
198 allowedClasses->num_values--;
199 i--;
204 return LDB_SUCCESS;
208 /* find all attributes allowed by all these objectClasses */
210 static int kludge_acl_callback(struct ldb_request *req, struct ldb_reply *ares)
212 struct kludge_acl_context *ac;
213 struct kludge_private_data *data;
214 int i, ret;
216 ac = talloc_get_type(req->context, struct kludge_acl_context);
217 data = talloc_get_type(ac->module->private_data, struct kludge_private_data);
219 if (!ares) {
220 return ldb_module_done(ac->req, NULL, NULL,
221 LDB_ERR_OPERATIONS_ERROR);
223 if (ares->error != LDB_SUCCESS) {
224 return ldb_module_done(ac->req, ares->controls,
225 ares->response, ares->error);
228 switch (ares->type) {
229 case LDB_REPLY_ENTRY:
230 if (ac->allowedAttributes) {
231 ret = kludge_acl_allowedAttributes(ac->module->ldb,
232 ares->message,
233 "allowedAttributes");
234 if (ret != LDB_SUCCESS) {
235 return ldb_module_done(ac->req, NULL, NULL, ret);
238 if (ac->allowedChildClasses) {
239 ret = kludge_acl_childClasses(ac->module->ldb,
240 ares->message,
241 "allowedChildClasses");
242 if (ret != LDB_SUCCESS) {
243 return ldb_module_done(ac->req, NULL, NULL, ret);
247 if (data && data->password_attrs) /* if we are not initialized just get through */
249 switch (ac->user_type) {
250 case SECURITY_SYSTEM:
251 if (ac->allowedAttributesEffective) {
252 ret = kludge_acl_allowedAttributes(ac->module->ldb, ares->message,
253 "allowedClassesAttributesEffective");
254 if (ret != LDB_SUCCESS) {
255 return ldb_module_done(ac->req, NULL, NULL, ret);
258 if (ac->allowedChildClassesEffective) {
259 ret = kludge_acl_childClasses(ac->module->ldb, ares->message,
260 "allowedClassesChildClassesEffective");
261 if (ret != LDB_SUCCESS) {
262 return ldb_module_done(ac->req, NULL, NULL, ret);
265 break;
267 case SECURITY_ADMINISTRATOR:
268 if (ac->allowedAttributesEffective) {
269 ret = kludge_acl_allowedAttributes(ac->module->ldb, ares->message,
270 "allowedClassesAttributesEffective");
271 if (ret != LDB_SUCCESS) {
272 return ldb_module_done(ac->req, NULL, NULL, ret);
275 if (ac->allowedChildClassesEffective) {
276 ret = kludge_acl_childClasses(ac->module->ldb, ares->message,
277 "allowedClassesChildClassesEffective");
278 if (ret != LDB_SUCCESS) {
279 return ldb_module_done(ac->req, NULL, NULL, ret);
282 /* fall through */
283 default:
284 /* remove password attributes */
285 for (i = 0; data->password_attrs[i]; i++) {
286 ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
291 if (ac->allowedAttributes ||
292 ac->allowedAttributesEffective ||
293 ac->allowedChildClasses ||
294 ac->allowedChildClassesEffective) {
296 if (!ldb_attr_in_list(ac->attrs, "objectClass") &&
297 !ldb_attr_in_list(ac->attrs, "*")) {
299 ldb_msg_remove_attr(ares->message,
300 "objectClass");
304 return ldb_module_send_entry(ac->req, ares->message);
306 case LDB_REPLY_REFERRAL:
307 return ldb_module_send_referral(ac->req, ares->referral);
309 case LDB_REPLY_DONE:
310 return ldb_module_done(ac->req, ares->controls,
311 ares->response, LDB_SUCCESS);
314 return LDB_SUCCESS;
317 static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
319 struct kludge_acl_context *ac;
320 struct ldb_request *down_req;
321 struct kludge_private_data *data;
322 const char * const *attrs;
323 int ret, i;
324 struct ldb_control *sd_control;
325 struct ldb_control **sd_saved_controls;
327 ac = talloc(req, struct kludge_acl_context);
328 if (ac == NULL) {
329 ldb_oom(module->ldb);
330 return LDB_ERR_OPERATIONS_ERROR;
333 data = talloc_get_type(module->private_data, struct kludge_private_data);
335 ac->module = module;
336 ac->req = req;
337 ac->user_type = what_is_user(module);
338 ac->attrs = req->op.search.attrs;
340 ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
342 ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
344 ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses");
346 ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
348 if (ac->allowedAttributes || ac->allowedAttributesEffective || ac->allowedChildClasses || ac->allowedChildClassesEffective) {
349 attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectClass");
350 } else {
351 attrs = req->op.search.attrs;
354 /* replace any attributes in the parse tree that are private,
355 so we don't allow a search for 'userPassword=penguin',
356 just as we would not allow that attribute to be returned */
357 switch (ac->user_type) {
358 case SECURITY_SYSTEM:
359 break;
360 default:
361 /* FIXME: We should copy the tree and keep the original unmodified. */
362 /* remove password attributes */
364 if (!data || !data->password_attrs) {
365 break;
367 for (i = 0; data->password_attrs[i]; i++) {
368 ldb_parse_tree_attr_replace(req->op.search.tree,
369 data->password_attrs[i],
370 "kludgeACLredactedattribute");
374 ret = ldb_build_search_req_ex(&down_req,
375 module->ldb, ac,
376 req->op.search.base,
377 req->op.search.scope,
378 req->op.search.tree,
379 attrs,
380 req->controls,
381 ac, kludge_acl_callback,
382 req);
383 if (ret != LDB_SUCCESS) {
384 return LDB_ERR_OPERATIONS_ERROR;
387 /* check if there's an SD_FLAGS control */
388 sd_control = ldb_request_get_control(down_req, LDB_CONTROL_SD_FLAGS_OID);
389 if (sd_control) {
390 /* save it locally and remove it from the list */
391 /* we do not need to replace them later as we
392 * are keeping the original req intact */
393 if (!save_controls(sd_control, down_req, &sd_saved_controls)) {
394 return LDB_ERR_OPERATIONS_ERROR;
398 /* perform the search */
399 return ldb_next_request(module, down_req);
402 /* ANY change type */
403 static int kludge_acl_change(struct ldb_module *module, struct ldb_request *req)
405 enum security_user_level user_type = what_is_user(module);
406 switch (user_type) {
407 case SECURITY_SYSTEM:
408 case SECURITY_ADMINISTRATOR:
409 return ldb_next_request(module, req);
410 default:
411 ldb_asprintf_errstring(module->ldb,
412 "kludge_acl_change: "
413 "attempted database modify not permitted. "
414 "User %s is not SYSTEM or an administrator",
415 user_name(req, module));
416 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
420 static int kludge_acl_init(struct ldb_module *module)
422 int ret, i;
423 TALLOC_CTX *mem_ctx = talloc_new(module);
424 static const char *attrs[] = { "passwordAttribute", NULL };
425 struct ldb_result *res;
426 struct ldb_message *msg;
427 struct ldb_message_element *password_attributes;
429 struct kludge_private_data *data;
431 data = talloc(module, struct kludge_private_data);
432 if (data == NULL) {
433 ldb_oom(module->ldb);
434 return LDB_ERR_OPERATIONS_ERROR;
437 data->password_attrs = NULL;
438 module->private_data = data;
440 if (!mem_ctx) {
441 ldb_oom(module->ldb);
442 return LDB_ERR_OPERATIONS_ERROR;
445 ret = ldb_search(module->ldb, mem_ctx, &res,
446 ldb_dn_new(mem_ctx, module->ldb, "@KLUDGEACL"),
447 LDB_SCOPE_BASE, attrs, NULL);
448 if (ret != LDB_SUCCESS) {
449 goto done;
451 if (res->count == 0) {
452 goto done;
455 if (res->count > 1) {
456 talloc_free(mem_ctx);
457 return LDB_ERR_CONSTRAINT_VIOLATION;
460 msg = res->msgs[0];
462 password_attributes = ldb_msg_find_element(msg, "passwordAttribute");
463 if (!password_attributes) {
464 goto done;
466 data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1);
467 if (!data->password_attrs) {
468 talloc_free(mem_ctx);
469 ldb_oom(module->ldb);
470 return LDB_ERR_OPERATIONS_ERROR;
472 for (i=0; i < password_attributes->num_values; i++) {
473 data->password_attrs[i] = (const char *)password_attributes->values[i].data;
474 talloc_steal(data->password_attrs, password_attributes->values[i].data);
476 data->password_attrs[i] = NULL;
478 ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
479 if (ret != LDB_SUCCESS) {
480 ldb_debug(module->ldb, LDB_DEBUG_ERROR,
481 "partition: Unable to register control with rootdse!\n");
482 return LDB_ERR_OPERATIONS_ERROR;
485 done:
486 talloc_free(mem_ctx);
487 return ldb_next_init(module);
490 _PUBLIC_ const struct ldb_module_ops ldb_kludge_acl_module_ops = {
491 .name = "kludge_acl",
492 .search = kludge_acl_search,
493 .add = kludge_acl_change,
494 .modify = kludge_acl_change,
495 .del = kludge_acl_change,
496 .rename = kludge_acl_change,
497 .extended = kludge_acl_change,
498 .init_context = kludge_acl_init