Version 1.0 of the directory service acls module.
[Samba/gebeck_regimport.git] / source4 / dsdb / samdb / ldb_modules / kludge_acl.c
blob0cec95b1c2b8786fc832679550595ab8182119c4
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"
38 #include "param/param.h"
40 /* Kludge ACL rules:
42 * - System can read passwords
43 * - Administrators can write anything
44 * - Users can read anything that is not a password
48 struct kludge_private_data {
49 const char **password_attrs;
50 bool acl_perform;
53 static enum security_user_level what_is_user(struct ldb_module *module)
55 struct ldb_context *ldb = ldb_module_get_ctx(module);
56 struct auth_session_info *session_info
57 = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
58 return security_session_user_level(session_info);
61 static const char *user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module)
63 struct ldb_context *ldb = ldb_module_get_ctx(module);
64 struct auth_session_info *session_info
65 = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
66 if (!session_info) {
67 return "UNKNOWN (NULL)";
70 return talloc_asprintf(mem_ctx, "%s\\%s",
71 session_info->server_info->domain_name,
72 session_info->server_info->account_name);
75 /* search */
76 struct kludge_acl_context {
78 struct ldb_module *module;
79 struct ldb_request *req;
81 enum security_user_level user_type;
82 bool allowedAttributes;
83 bool allowedAttributesEffective;
84 bool allowedChildClasses;
85 bool allowedChildClassesEffective;
86 const char * const *attrs;
89 /* read all objectClasses */
91 static int kludge_acl_allowedAttributes(struct ldb_context *ldb, struct ldb_message *msg,
92 const char *attrName)
94 struct ldb_message_element *oc_el;
95 struct ldb_message_element *allowedAttributes;
96 const struct dsdb_schema *schema = dsdb_get_schema(ldb);
97 TALLOC_CTX *mem_ctx;
98 const char **attr_list;
99 int i, ret;
101 /* If we don't have a schema yet, we can't do anything... */
102 if (schema == NULL) {
103 return LDB_SUCCESS;
106 /* Must remove any existing attribute, or else confusion reins */
107 ldb_msg_remove_attr(msg, attrName);
108 ret = ldb_msg_add_empty(msg, attrName, 0, &allowedAttributes);
109 if (ret != LDB_SUCCESS) {
110 return ret;
113 mem_ctx = talloc_new(msg);
114 if (!mem_ctx) {
115 ldb_oom(ldb);
116 return LDB_ERR_OPERATIONS_ERROR;
119 /* To ensure that oc_el is valid, we must look for it after
120 we alter the element array in ldb_msg_add_empty() */
121 oc_el = ldb_msg_find_element(msg, "objectClass");
123 attr_list = dsdb_full_attribute_list(mem_ctx, schema, oc_el, DSDB_SCHEMA_ALL);
124 if (!attr_list) {
125 ldb_asprintf_errstring(ldb, "kludge_acl: Failed to get list of attributes create %s attribute", attrName);
126 talloc_free(mem_ctx);
127 return LDB_ERR_OPERATIONS_ERROR;
130 for (i=0; attr_list && attr_list[i]; i++) {
131 ldb_msg_add_string(msg, attrName, attr_list[i]);
133 talloc_free(mem_ctx);
134 return LDB_SUCCESS;
137 /* read all objectClasses */
139 static int kludge_acl_childClasses(struct ldb_context *ldb, struct ldb_message *msg,
140 const char *attrName)
142 struct ldb_message_element *oc_el;
143 struct ldb_message_element *allowedClasses;
144 const struct dsdb_schema *schema = dsdb_get_schema(ldb);
145 const struct dsdb_class *sclass;
146 int i, j, ret;
148 /* If we don't have a schema yet, we can't do anything... */
149 if (schema == NULL) {
150 return LDB_SUCCESS;
153 /* Must remove any existing attribute, or else confusion reins */
154 ldb_msg_remove_attr(msg, attrName);
155 ret = ldb_msg_add_empty(msg, attrName, 0, &allowedClasses);
156 if (ret != LDB_SUCCESS) {
157 return ret;
160 /* To ensure that oc_el is valid, we must look for it after
161 we alter the element array in ldb_msg_add_empty() */
162 oc_el = ldb_msg_find_element(msg, "objectClass");
164 for (i=0; oc_el && i < oc_el->num_values; i++) {
165 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
166 if (!sclass) {
167 /* We don't know this class? what is going on? */
168 continue;
171 for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
172 ldb_msg_add_string(msg, attrName, sclass->possibleInferiors[j]);
176 if (allowedClasses->num_values > 1) {
177 qsort(allowedClasses->values,
178 allowedClasses->num_values,
179 sizeof(*allowedClasses->values),
180 (comparison_fn_t)data_blob_cmp);
182 for (i=1 ; i < allowedClasses->num_values; i++) {
184 struct ldb_val *val1 = &allowedClasses->values[i-1];
185 struct ldb_val *val2 = &allowedClasses->values[i];
186 if (data_blob_cmp(val1, val2) == 0) {
187 memmove(val1, val2, (allowedClasses->num_values - i) * sizeof( struct ldb_val));
188 allowedClasses->num_values--;
189 i--;
194 return LDB_SUCCESS;
198 /* find all attributes allowed by all these objectClasses */
200 static int kludge_acl_callback(struct ldb_request *req, struct ldb_reply *ares)
202 struct ldb_context *ldb;
203 struct kludge_acl_context *ac;
204 struct kludge_private_data *data;
205 int i, ret;
207 ac = talloc_get_type(req->context, struct kludge_acl_context);
208 data = talloc_get_type(ldb_module_get_private(ac->module), struct kludge_private_data);
209 ldb = ldb_module_get_ctx(ac->module);
211 if (!ares) {
212 return ldb_module_done(ac->req, NULL, NULL,
213 LDB_ERR_OPERATIONS_ERROR);
215 if (ares->error != LDB_SUCCESS) {
216 return ldb_module_done(ac->req, ares->controls,
217 ares->response, ares->error);
220 switch (ares->type) {
221 case LDB_REPLY_ENTRY:
222 if (ac->allowedAttributes) {
223 ret = kludge_acl_allowedAttributes(ldb,
224 ares->message,
225 "allowedAttributes");
226 if (ret != LDB_SUCCESS) {
227 return ldb_module_done(ac->req, NULL, NULL, ret);
230 if (ac->allowedChildClasses) {
231 ret = kludge_acl_childClasses(ldb,
232 ares->message,
233 "allowedChildClasses");
234 if (ret != LDB_SUCCESS) {
235 return ldb_module_done(ac->req, NULL, NULL, ret);
239 if (data && data->password_attrs) /* if we are not initialized just get through */
241 switch (ac->user_type) {
242 case SECURITY_SYSTEM:
243 if (ac->allowedAttributesEffective) {
244 ret = kludge_acl_allowedAttributes(ldb, ares->message,
245 "allowedAttributesEffective");
246 if (ret != LDB_SUCCESS) {
247 return ldb_module_done(ac->req, NULL, NULL, ret);
250 if (ac->allowedChildClassesEffective) {
251 ret = kludge_acl_childClasses(ldb, ares->message,
252 "allowedChildClassesEffective");
253 if (ret != LDB_SUCCESS) {
254 return ldb_module_done(ac->req, NULL, NULL, ret);
257 break;
259 case SECURITY_ADMINISTRATOR:
260 if (ac->allowedAttributesEffective) {
261 ret = kludge_acl_allowedAttributes(ldb, ares->message,
262 "allowedAttributesEffective");
263 if (ret != LDB_SUCCESS) {
264 return ldb_module_done(ac->req, NULL, NULL, ret);
267 if (ac->allowedChildClassesEffective) {
268 ret = kludge_acl_childClasses(ldb, ares->message,
269 "allowedChildClassesEffective");
270 if (ret != LDB_SUCCESS) {
271 return ldb_module_done(ac->req, NULL, NULL, ret);
274 /* fall through */
275 default:
276 /* remove password attributes */
277 for (i = 0; data->password_attrs[i]; i++) {
278 ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
283 if (ac->allowedAttributes ||
284 ac->allowedAttributesEffective ||
285 ac->allowedChildClasses ||
286 ac->allowedChildClassesEffective) {
288 if (!ldb_attr_in_list(ac->attrs, "objectClass") &&
289 !ldb_attr_in_list(ac->attrs, "*")) {
291 ldb_msg_remove_attr(ares->message,
292 "objectClass");
296 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
298 case LDB_REPLY_REFERRAL:
299 return ldb_module_send_referral(ac->req, ares->referral);
301 case LDB_REPLY_DONE:
302 return ldb_module_done(ac->req, ares->controls,
303 ares->response, LDB_SUCCESS);
306 return LDB_SUCCESS;
309 static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
311 struct ldb_context *ldb;
312 struct kludge_acl_context *ac;
313 struct ldb_request *down_req;
314 struct kludge_private_data *data;
315 const char * const *attrs;
316 int ret, i;
317 struct ldb_control *sd_control;
318 struct ldb_control **sd_saved_controls;
320 ldb = ldb_module_get_ctx(module);
322 ac = talloc(req, struct kludge_acl_context);
323 if (ac == NULL) {
324 ldb_oom(ldb);
325 return LDB_ERR_OPERATIONS_ERROR;
328 data = talloc_get_type(ldb_module_get_private(module), struct kludge_private_data);
330 if (data && data->acl_perform)
331 return ldb_next_request(module, req);
333 ac->module = module;
334 ac->req = req;
335 ac->user_type = what_is_user(module);
336 ac->attrs = req->op.search.attrs;
338 ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
340 ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
342 ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses");
344 ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
346 if (ac->allowedAttributes || ac->allowedAttributesEffective || ac->allowedChildClasses || ac->allowedChildClassesEffective) {
347 attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectClass");
348 } else {
349 attrs = req->op.search.attrs;
352 /* replace any attributes in the parse tree that are private,
353 so we don't allow a search for 'userPassword=penguin',
354 just as we would not allow that attribute to be returned */
355 switch (ac->user_type) {
356 case SECURITY_SYSTEM:
357 break;
358 default:
359 /* FIXME: We should copy the tree and keep the original unmodified. */
360 /* remove password attributes */
362 if (!data || !data->password_attrs) {
363 break;
365 for (i = 0; data->password_attrs[i]; i++) {
366 ldb_parse_tree_attr_replace(req->op.search.tree,
367 data->password_attrs[i],
368 "kludgeACLredactedattribute");
372 ret = ldb_build_search_req_ex(&down_req,
373 ldb, ac,
374 req->op.search.base,
375 req->op.search.scope,
376 req->op.search.tree,
377 attrs,
378 req->controls,
379 ac, kludge_acl_callback,
380 req);
381 if (ret != LDB_SUCCESS) {
382 return ret;
385 /* check if there's an SD_FLAGS control */
386 sd_control = ldb_request_get_control(down_req, LDB_CONTROL_SD_FLAGS_OID);
387 if (sd_control) {
388 /* save it locally and remove it from the list */
389 /* we do not need to replace them later as we
390 * are keeping the original req intact */
391 if (!save_controls(sd_control, down_req, &sd_saved_controls)) {
392 return LDB_ERR_OPERATIONS_ERROR;
396 /* perform the search */
397 return ldb_next_request(module, down_req);
400 /* ANY change type */
401 static int kludge_acl_change(struct ldb_module *module, struct ldb_request *req)
403 struct ldb_context *ldb = ldb_module_get_ctx(module);
404 enum security_user_level user_type = what_is_user(module);
405 struct kludge_private_data *data = talloc_get_type(ldb_module_get_private(module),
406 struct kludge_private_data);
408 if (data->acl_perform)
409 return ldb_next_request(module, req);
411 switch (user_type) {
412 case SECURITY_SYSTEM:
413 case SECURITY_ADMINISTRATOR:
414 return ldb_next_request(module, req);
415 default:
416 ldb_asprintf_errstring(ldb,
417 "kludge_acl_change: "
418 "attempted database modify not permitted. "
419 "User %s is not SYSTEM or an administrator",
420 user_name(req, module));
421 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
425 static int kludge_acl_extended(struct ldb_module *module, struct ldb_request *req)
427 struct ldb_context *ldb = ldb_module_get_ctx(module);
428 enum security_user_level user_type;
430 /* allow everybody to read the sequence number */
431 if (strcmp(req->op.extended.oid,
432 LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
433 return ldb_next_request(module, req);
436 user_type = what_is_user(module);
438 switch (user_type) {
439 case SECURITY_SYSTEM:
440 case SECURITY_ADMINISTRATOR:
441 return ldb_next_request(module, req);
442 default:
443 ldb_asprintf_errstring(ldb,
444 "kludge_acl_change: "
445 "attempted database modify not permitted. "
446 "User %s is not SYSTEM or an administrator",
447 user_name(req, module));
448 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
452 static int kludge_acl_init(struct ldb_module *module)
454 struct ldb_context *ldb;
455 int ret, i;
456 TALLOC_CTX *mem_ctx = talloc_new(module);
457 static const char *attrs[] = { "passwordAttribute", NULL };
458 struct ldb_result *res;
459 struct ldb_message *msg;
460 struct ldb_message_element *password_attributes;
462 struct kludge_private_data *data;
464 ldb = ldb_module_get_ctx(module);
466 data = talloc(module, struct kludge_private_data);
467 if (data == NULL) {
468 ldb_oom(ldb);
469 return LDB_ERR_OPERATIONS_ERROR;
472 data->password_attrs = NULL;
473 data->acl_perform = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"),
474 NULL, "acl", "perform", false);
475 ldb_module_set_private(module, data);
477 if (!mem_ctx) {
478 ldb_oom(ldb);
479 return LDB_ERR_OPERATIONS_ERROR;
482 ret = ldb_search(ldb, mem_ctx, &res,
483 ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"),
484 LDB_SCOPE_BASE, attrs, NULL);
485 if (ret != LDB_SUCCESS) {
486 goto done;
488 if (res->count == 0) {
489 goto done;
492 if (res->count > 1) {
493 talloc_free(mem_ctx);
494 return LDB_ERR_CONSTRAINT_VIOLATION;
497 msg = res->msgs[0];
499 password_attributes = ldb_msg_find_element(msg, "passwordAttribute");
500 if (!password_attributes) {
501 goto done;
503 data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1);
504 if (!data->password_attrs) {
505 talloc_free(mem_ctx);
506 ldb_oom(ldb);
507 return LDB_ERR_OPERATIONS_ERROR;
509 for (i=0; i < password_attributes->num_values; i++) {
510 data->password_attrs[i] = (const char *)password_attributes->values[i].data;
511 talloc_steal(data->password_attrs, password_attributes->values[i].data);
513 data->password_attrs[i] = NULL;
515 ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
516 if (ret != LDB_SUCCESS) {
517 ldb_debug(ldb, LDB_DEBUG_ERROR,
518 "kludge_acl: Unable to register control with rootdse!\n");
519 return LDB_ERR_OPERATIONS_ERROR;
522 done:
523 talloc_free(mem_ctx);
524 return ldb_next_init(module);
527 _PUBLIC_ const struct ldb_module_ops ldb_kludge_acl_module_ops = {
528 .name = "kludge_acl",
529 .search = kludge_acl_search,
530 /* .add = kludge_acl_change,
531 .modify = kludge_acl_change,
532 .del = kludge_acl_change,
533 .rename = kludge_acl_change, */
534 .extended = kludge_acl_extended,
535 .init_context = kludge_acl_init