s4:dsdb Don't cast an ldb_val into a const char * for schema lookups
[Samba/ekacnet.git] / source4 / dsdb / samdb / ldb_modules / kludge_acl.c
blob15db491171f3e3045e6987be966ad054ac0965d9
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_module.h"
35 #include "auth/auth.h"
36 #include "libcli/security/security.h"
37 #include "dsdb/samdb/samdb.h"
39 /* Kludge ACL rules:
41 * - System can read passwords
42 * - Administrators can write anything
43 * - Users can read anything that is not a password
47 struct kludge_private_data {
48 const char **password_attrs;
51 static enum security_user_level what_is_user(struct ldb_module *module)
53 struct ldb_context *ldb = ldb_module_get_ctx(module);
54 struct auth_session_info *session_info
55 = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
56 return security_session_user_level(session_info);
59 static const char *user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module)
61 struct ldb_context *ldb = ldb_module_get_ctx(module);
62 struct auth_session_info *session_info
63 = (struct auth_session_info *)ldb_get_opaque(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 const char **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 attr_list = dsdb_full_attribute_list(mem_ctx, schema, oc_el, DSDB_SCHEMA_ALL);
122 if (!attr_list) {
123 ldb_asprintf_errstring(ldb, "kludge_acl: Failed to get list of attributes create %s attribute", attrName);
124 talloc_free(mem_ctx);
125 return LDB_ERR_OPERATIONS_ERROR;
128 for (i=0; attr_list && attr_list[i]; i++) {
129 ldb_msg_add_string(msg, attrName, attr_list[i]);
131 talloc_free(mem_ctx);
132 return LDB_SUCCESS;
135 /* read all objectClasses */
137 static int kludge_acl_childClasses(struct ldb_context *ldb, struct ldb_message *msg,
138 const char *attrName)
140 struct ldb_message_element *oc_el;
141 struct ldb_message_element *allowedClasses;
142 const struct dsdb_schema *schema = dsdb_get_schema(ldb);
143 const struct dsdb_class *sclass;
144 int i, j, ret;
146 /* If we don't have a schema yet, we can't do anything... */
147 if (schema == NULL) {
148 return LDB_SUCCESS;
151 /* Must remove any existing attribute, or else confusion reins */
152 ldb_msg_remove_attr(msg, attrName);
153 ret = ldb_msg_add_empty(msg, attrName, 0, &allowedClasses);
154 if (ret != LDB_SUCCESS) {
155 return ret;
158 /* To ensure that oc_el is valid, we must look for it after
159 we alter the element array in ldb_msg_add_empty() */
160 oc_el = ldb_msg_find_element(msg, "objectClass");
162 for (i=0; oc_el && i < oc_el->num_values; i++) {
163 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
164 if (!sclass) {
165 /* We don't know this class? what is going on? */
166 continue;
169 for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
170 ldb_msg_add_string(msg, attrName, sclass->possibleInferiors[j]);
174 if (allowedClasses->num_values > 1) {
175 qsort(allowedClasses->values,
176 allowedClasses->num_values,
177 sizeof(*allowedClasses->values),
178 (comparison_fn_t)data_blob_cmp);
180 for (i=1 ; i < allowedClasses->num_values; i++) {
182 struct ldb_val *val1 = &allowedClasses->values[i-1];
183 struct ldb_val *val2 = &allowedClasses->values[i];
184 if (data_blob_cmp(val1, val2) == 0) {
185 memmove(val1, val2, (allowedClasses->num_values - i) * sizeof( struct ldb_val));
186 allowedClasses->num_values--;
187 i--;
192 return LDB_SUCCESS;
196 /* find all attributes allowed by all these objectClasses */
198 static int kludge_acl_callback(struct ldb_request *req, struct ldb_reply *ares)
200 struct ldb_context *ldb;
201 struct kludge_acl_context *ac;
202 struct kludge_private_data *data;
203 int i, ret;
205 ac = talloc_get_type(req->context, struct kludge_acl_context);
206 data = talloc_get_type(ldb_module_get_private(ac->module), struct kludge_private_data);
207 ldb = ldb_module_get_ctx(ac->module);
209 if (!ares) {
210 return ldb_module_done(ac->req, NULL, NULL,
211 LDB_ERR_OPERATIONS_ERROR);
213 if (ares->error != LDB_SUCCESS) {
214 return ldb_module_done(ac->req, ares->controls,
215 ares->response, ares->error);
218 switch (ares->type) {
219 case LDB_REPLY_ENTRY:
220 if (ac->allowedAttributes) {
221 ret = kludge_acl_allowedAttributes(ldb,
222 ares->message,
223 "allowedAttributes");
224 if (ret != LDB_SUCCESS) {
225 return ldb_module_done(ac->req, NULL, NULL, ret);
228 if (ac->allowedChildClasses) {
229 ret = kludge_acl_childClasses(ldb,
230 ares->message,
231 "allowedChildClasses");
232 if (ret != LDB_SUCCESS) {
233 return ldb_module_done(ac->req, NULL, NULL, ret);
237 if (data && data->password_attrs) /* if we are not initialized just get through */
239 switch (ac->user_type) {
240 case SECURITY_SYSTEM:
241 if (ac->allowedAttributesEffective) {
242 ret = kludge_acl_allowedAttributes(ldb, ares->message,
243 "allowedAttributesEffective");
244 if (ret != LDB_SUCCESS) {
245 return ldb_module_done(ac->req, NULL, NULL, ret);
248 if (ac->allowedChildClassesEffective) {
249 ret = kludge_acl_childClasses(ldb, ares->message,
250 "allowedChildClassesEffective");
251 if (ret != LDB_SUCCESS) {
252 return ldb_module_done(ac->req, NULL, NULL, ret);
255 break;
257 case SECURITY_ADMINISTRATOR:
258 if (ac->allowedAttributesEffective) {
259 ret = kludge_acl_allowedAttributes(ldb, ares->message,
260 "allowedAttributesEffective");
261 if (ret != LDB_SUCCESS) {
262 return ldb_module_done(ac->req, NULL, NULL, ret);
265 if (ac->allowedChildClassesEffective) {
266 ret = kludge_acl_childClasses(ldb, ares->message,
267 "allowedChildClassesEffective");
268 if (ret != LDB_SUCCESS) {
269 return ldb_module_done(ac->req, NULL, NULL, ret);
272 /* fall through */
273 default:
274 /* remove password attributes */
275 for (i = 0; data->password_attrs[i]; i++) {
276 ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
281 if (ac->allowedAttributes ||
282 ac->allowedAttributesEffective ||
283 ac->allowedChildClasses ||
284 ac->allowedChildClassesEffective) {
286 if (!ldb_attr_in_list(ac->attrs, "objectClass") &&
287 !ldb_attr_in_list(ac->attrs, "*")) {
289 ldb_msg_remove_attr(ares->message,
290 "objectClass");
294 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
296 case LDB_REPLY_REFERRAL:
297 return ldb_module_send_referral(ac->req, ares->referral);
299 case LDB_REPLY_DONE:
300 return ldb_module_done(ac->req, ares->controls,
301 ares->response, LDB_SUCCESS);
304 return LDB_SUCCESS;
307 static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
309 struct ldb_context *ldb;
310 struct kludge_acl_context *ac;
311 struct ldb_request *down_req;
312 struct kludge_private_data *data;
313 const char * const *attrs;
314 int ret, i;
315 struct ldb_control *sd_control;
316 struct ldb_control **sd_saved_controls;
318 ldb = ldb_module_get_ctx(module);
320 ac = talloc(req, struct kludge_acl_context);
321 if (ac == NULL) {
322 ldb_oom(ldb);
323 return LDB_ERR_OPERATIONS_ERROR;
326 data = talloc_get_type(ldb_module_get_private(module), struct kludge_private_data);
328 ac->module = module;
329 ac->req = req;
330 ac->user_type = what_is_user(module);
331 ac->attrs = req->op.search.attrs;
333 ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
335 ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
337 ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses");
339 ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
341 if (ac->allowedAttributes || ac->allowedAttributesEffective || ac->allowedChildClasses || ac->allowedChildClassesEffective) {
342 attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectClass");
343 } else {
344 attrs = req->op.search.attrs;
347 /* replace any attributes in the parse tree that are private,
348 so we don't allow a search for 'userPassword=penguin',
349 just as we would not allow that attribute to be returned */
350 switch (ac->user_type) {
351 case SECURITY_SYSTEM:
352 break;
353 default:
354 /* FIXME: We should copy the tree and keep the original unmodified. */
355 /* remove password attributes */
357 if (!data || !data->password_attrs) {
358 break;
360 for (i = 0; data->password_attrs[i]; i++) {
361 ldb_parse_tree_attr_replace(req->op.search.tree,
362 data->password_attrs[i],
363 "kludgeACLredactedattribute");
367 ret = ldb_build_search_req_ex(&down_req,
368 ldb, ac,
369 req->op.search.base,
370 req->op.search.scope,
371 req->op.search.tree,
372 attrs,
373 req->controls,
374 ac, kludge_acl_callback,
375 req);
376 if (ret != LDB_SUCCESS) {
377 return LDB_ERR_OPERATIONS_ERROR;
380 /* check if there's an SD_FLAGS control */
381 sd_control = ldb_request_get_control(down_req, LDB_CONTROL_SD_FLAGS_OID);
382 if (sd_control) {
383 /* save it locally and remove it from the list */
384 /* we do not need to replace them later as we
385 * are keeping the original req intact */
386 if (!save_controls(sd_control, down_req, &sd_saved_controls)) {
387 return LDB_ERR_OPERATIONS_ERROR;
391 /* perform the search */
392 return ldb_next_request(module, down_req);
395 /* ANY change type */
396 static int kludge_acl_change(struct ldb_module *module, struct ldb_request *req)
398 struct ldb_context *ldb = ldb_module_get_ctx(module);
399 enum security_user_level user_type = what_is_user(module);
400 switch (user_type) {
401 case SECURITY_SYSTEM:
402 case SECURITY_ADMINISTRATOR:
403 return ldb_next_request(module, req);
404 default:
405 ldb_asprintf_errstring(ldb,
406 "kludge_acl_change: "
407 "attempted database modify not permitted. "
408 "User %s is not SYSTEM or an administrator",
409 user_name(req, module));
410 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
414 static int kludge_acl_extended(struct ldb_module *module, struct ldb_request *req)
416 struct ldb_context *ldb = ldb_module_get_ctx(module);
417 enum security_user_level user_type;
419 /* allow everybody to read the sequence number */
420 if (strcmp(req->op.extended.oid,
421 LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
422 return ldb_next_request(module, req);
425 user_type = what_is_user(module);
427 switch (user_type) {
428 case SECURITY_SYSTEM:
429 case SECURITY_ADMINISTRATOR:
430 return ldb_next_request(module, req);
431 default:
432 ldb_asprintf_errstring(ldb,
433 "kludge_acl_change: "
434 "attempted database modify not permitted. "
435 "User %s is not SYSTEM or an administrator",
436 user_name(req, module));
437 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
441 static int kludge_acl_init(struct ldb_module *module)
443 struct ldb_context *ldb;
444 int ret, i;
445 TALLOC_CTX *mem_ctx = talloc_new(module);
446 static const char *attrs[] = { "passwordAttribute", NULL };
447 struct ldb_result *res;
448 struct ldb_message *msg;
449 struct ldb_message_element *password_attributes;
451 struct kludge_private_data *data;
453 ldb = ldb_module_get_ctx(module);
455 data = talloc(module, struct kludge_private_data);
456 if (data == NULL) {
457 ldb_oom(ldb);
458 return LDB_ERR_OPERATIONS_ERROR;
461 data->password_attrs = NULL;
462 ldb_module_set_private(module, data);
464 if (!mem_ctx) {
465 ldb_oom(ldb);
466 return LDB_ERR_OPERATIONS_ERROR;
469 ret = ldb_search(ldb, mem_ctx, &res,
470 ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"),
471 LDB_SCOPE_BASE, attrs, NULL);
472 if (ret != LDB_SUCCESS) {
473 goto done;
475 if (res->count == 0) {
476 goto done;
479 if (res->count > 1) {
480 talloc_free(mem_ctx);
481 return LDB_ERR_CONSTRAINT_VIOLATION;
484 msg = res->msgs[0];
486 password_attributes = ldb_msg_find_element(msg, "passwordAttribute");
487 if (!password_attributes) {
488 goto done;
490 data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1);
491 if (!data->password_attrs) {
492 talloc_free(mem_ctx);
493 ldb_oom(ldb);
494 return LDB_ERR_OPERATIONS_ERROR;
496 for (i=0; i < password_attributes->num_values; i++) {
497 data->password_attrs[i] = (const char *)password_attributes->values[i].data;
498 talloc_steal(data->password_attrs, password_attributes->values[i].data);
500 data->password_attrs[i] = NULL;
502 ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
503 if (ret != LDB_SUCCESS) {
504 ldb_debug(ldb, LDB_DEBUG_ERROR,
505 "kludge_acl: Unable to register control with rootdse!\n");
506 return LDB_ERR_OPERATIONS_ERROR;
509 done:
510 talloc_free(mem_ctx);
511 return ldb_next_init(module);
514 _PUBLIC_ const struct ldb_module_ops ldb_kludge_acl_module_ops = {
515 .name = "kludge_acl",
516 .search = kludge_acl_search,
517 .add = kludge_acl_change,
518 .modify = kludge_acl_change,
519 .del = kludge_acl_change,
520 .rename = kludge_acl_change,
521 .extended = kludge_acl_extended,
522 .init_context = kludge_acl_init