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/>.
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
30
* Author: Andrew Bartlett
34
#include "ldb_module.h"
35
#include "auth/auth.h"
36
#include "libcli/security/security.h"
37
#include "dsdb/samdb/samdb.h"
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");
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);
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,
92
struct ldb_message_element *oc_el;
93
struct ldb_message_element *allowedAttributes;
94
const struct dsdb_schema *schema = dsdb_get_schema(ldb);
96
const char **objectclass_list, **attr_list;
99
/* If we don't have a schema yet, we can't do anything... */
100
if (schema == NULL) {
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) {
111
mem_ctx = talloc_new(msg);
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, const char *, oc_el->num_values + 1);
122
if (!objectclass_list) {
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] = (const char *)oc_el->values[i].data;
131
objectclass_list[i] = NULL;
133
attr_list = dsdb_full_attribute_list(mem_ctx, schema, objectclass_list, DSDB_SCHEMA_ALL);
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);
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 *sclass;
158
/* If we don't have a schema yet, we can't do anything... */
159
if (schema == NULL) {
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) {
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
sclass = dsdb_class_by_lDAPDisplayName(schema, (const char *)oc_el->values[i].data);
177
/* We don't know this class? what is going on? */
181
for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
182
ldb_msg_add_string(msg, attrName, sclass->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--;
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 ldb_context *ldb;
213
struct kludge_acl_context *ac;
214
struct kludge_private_data *data;
217
ac = talloc_get_type(req->context, struct kludge_acl_context);
218
data = talloc_get_type(ldb_module_get_private(ac->module), struct kludge_private_data);
219
ldb = ldb_module_get_ctx(ac->module);
222
return ldb_module_done(ac->req, NULL, NULL,
223
LDB_ERR_OPERATIONS_ERROR);
225
if (ares->error != LDB_SUCCESS) {
226
return ldb_module_done(ac->req, ares->controls,
227
ares->response, ares->error);
230
switch (ares->type) {
231
case LDB_REPLY_ENTRY:
232
if (ac->allowedAttributes) {
233
ret = kludge_acl_allowedAttributes(ldb,
235
"allowedAttributes");
236
if (ret != LDB_SUCCESS) {
237
return ldb_module_done(ac->req, NULL, NULL, ret);
240
if (ac->allowedChildClasses) {
241
ret = kludge_acl_childClasses(ldb,
243
"allowedChildClasses");
244
if (ret != LDB_SUCCESS) {
245
return ldb_module_done(ac->req, NULL, NULL, ret);
249
if (data && data->password_attrs) /* if we are not initialized just get through */
251
switch (ac->user_type) {
252
case SECURITY_SYSTEM:
253
if (ac->allowedAttributesEffective) {
254
ret = kludge_acl_allowedAttributes(ldb, ares->message,
255
"allowedAttributesEffective");
256
if (ret != LDB_SUCCESS) {
257
return ldb_module_done(ac->req, NULL, NULL, ret);
260
if (ac->allowedChildClassesEffective) {
261
ret = kludge_acl_childClasses(ldb, ares->message,
262
"allowedChildClassesEffective");
263
if (ret != LDB_SUCCESS) {
264
return ldb_module_done(ac->req, NULL, NULL, ret);
269
case SECURITY_ADMINISTRATOR:
270
if (ac->allowedAttributesEffective) {
271
ret = kludge_acl_allowedAttributes(ldb, ares->message,
272
"allowedAttributesEffective");
273
if (ret != LDB_SUCCESS) {
274
return ldb_module_done(ac->req, NULL, NULL, ret);
277
if (ac->allowedChildClassesEffective) {
278
ret = kludge_acl_childClasses(ldb, ares->message,
279
"allowedChildClassesEffective");
280
if (ret != LDB_SUCCESS) {
281
return ldb_module_done(ac->req, NULL, NULL, ret);
286
/* remove password attributes */
287
for (i = 0; data->password_attrs[i]; i++) {
288
ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
293
if (ac->allowedAttributes ||
294
ac->allowedAttributesEffective ||
295
ac->allowedChildClasses ||
296
ac->allowedChildClassesEffective) {
298
if (!ldb_attr_in_list(ac->attrs, "objectClass") &&
299
!ldb_attr_in_list(ac->attrs, "*")) {
301
ldb_msg_remove_attr(ares->message,
306
return ldb_module_send_entry(ac->req, ares->message, ares->controls);
308
case LDB_REPLY_REFERRAL:
309
return ldb_module_send_referral(ac->req, ares->referral);
312
return ldb_module_done(ac->req, ares->controls,
313
ares->response, LDB_SUCCESS);
319
static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
321
struct ldb_context *ldb;
322
struct kludge_acl_context *ac;
323
struct ldb_request *down_req;
324
struct kludge_private_data *data;
325
const char * const *attrs;
327
struct ldb_control *sd_control;
328
struct ldb_control **sd_saved_controls;
330
ldb = ldb_module_get_ctx(module);
332
ac = talloc(req, struct kludge_acl_context);
335
return LDB_ERR_OPERATIONS_ERROR;
338
data = talloc_get_type(ldb_module_get_private(module), struct kludge_private_data);
342
ac->user_type = what_is_user(module);
343
ac->attrs = req->op.search.attrs;
345
ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
347
ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
349
ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses");
351
ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
353
if (ac->allowedAttributes || ac->allowedAttributesEffective || ac->allowedChildClasses || ac->allowedChildClassesEffective) {
354
attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectClass");
356
attrs = req->op.search.attrs;
359
/* replace any attributes in the parse tree that are private,
360
so we don't allow a search for 'userPassword=penguin',
361
just as we would not allow that attribute to be returned */
362
switch (ac->user_type) {
363
case SECURITY_SYSTEM:
366
/* FIXME: We should copy the tree and keep the original unmodified. */
367
/* remove password attributes */
369
if (!data || !data->password_attrs) {
372
for (i = 0; data->password_attrs[i]; i++) {
373
ldb_parse_tree_attr_replace(req->op.search.tree,
374
data->password_attrs[i],
375
"kludgeACLredactedattribute");
379
ret = ldb_build_search_req_ex(&down_req,
382
req->op.search.scope,
386
ac, kludge_acl_callback,
388
if (ret != LDB_SUCCESS) {
389
return LDB_ERR_OPERATIONS_ERROR;
392
/* check if there's an SD_FLAGS control */
393
sd_control = ldb_request_get_control(down_req, LDB_CONTROL_SD_FLAGS_OID);
395
/* save it locally and remove it from the list */
396
/* we do not need to replace them later as we
397
* are keeping the original req intact */
398
if (!save_controls(sd_control, down_req, &sd_saved_controls)) {
399
return LDB_ERR_OPERATIONS_ERROR;
403
/* perform the search */
404
return ldb_next_request(module, down_req);
407
/* ANY change type */
408
static int kludge_acl_change(struct ldb_module *module, struct ldb_request *req)
410
struct ldb_context *ldb = ldb_module_get_ctx(module);
411
enum security_user_level user_type = what_is_user(module);
413
case SECURITY_SYSTEM:
414
case SECURITY_ADMINISTRATOR:
415
return ldb_next_request(module, req);
417
ldb_asprintf_errstring(ldb,
418
"kludge_acl_change: "
419
"attempted database modify not permitted. "
420
"User %s is not SYSTEM or an administrator",
421
user_name(req, module));
422
return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
426
static int kludge_acl_extended(struct ldb_module *module, struct ldb_request *req)
428
struct ldb_context *ldb = ldb_module_get_ctx(module);
429
enum security_user_level user_type;
431
/* allow everybody to read the sequence number */
432
if (strcmp(req->op.extended.oid,
433
LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
434
return ldb_next_request(module, req);
437
user_type = what_is_user(module);
440
case SECURITY_SYSTEM:
441
case SECURITY_ADMINISTRATOR:
442
return ldb_next_request(module, req);
444
ldb_asprintf_errstring(ldb,
445
"kludge_acl_change: "
446
"attempted database modify not permitted. "
447
"User %s is not SYSTEM or an administrator",
448
user_name(req, module));
449
return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
453
static int kludge_acl_init(struct ldb_module *module)
455
struct ldb_context *ldb;
457
TALLOC_CTX *mem_ctx = talloc_new(module);
458
static const char *attrs[] = { "passwordAttribute", NULL };
459
struct ldb_result *res;
460
struct ldb_message *msg;
461
struct ldb_message_element *password_attributes;
463
struct kludge_private_data *data;
465
ldb = ldb_module_get_ctx(module);
467
data = talloc(module, struct kludge_private_data);
470
return LDB_ERR_OPERATIONS_ERROR;
473
data->password_attrs = NULL;
474
ldb_module_set_private(module, data);
478
return LDB_ERR_OPERATIONS_ERROR;
481
ret = ldb_search(ldb, mem_ctx, &res,
482
ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"),
483
LDB_SCOPE_BASE, attrs, NULL);
484
if (ret != LDB_SUCCESS) {
487
if (res->count == 0) {
491
if (res->count > 1) {
492
talloc_free(mem_ctx);
493
return LDB_ERR_CONSTRAINT_VIOLATION;
498
password_attributes = ldb_msg_find_element(msg, "passwordAttribute");
499
if (!password_attributes) {
502
data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1);
503
if (!data->password_attrs) {
504
talloc_free(mem_ctx);
506
return LDB_ERR_OPERATIONS_ERROR;
508
for (i=0; i < password_attributes->num_values; i++) {
509
data->password_attrs[i] = (const char *)password_attributes->values[i].data;
510
talloc_steal(data->password_attrs, password_attributes->values[i].data);
512
data->password_attrs[i] = NULL;
514
ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
515
if (ret != LDB_SUCCESS) {
516
ldb_debug(ldb, LDB_DEBUG_ERROR,
517
"partition: Unable to register control with rootdse!\n");
518
return LDB_ERR_OPERATIONS_ERROR;
522
talloc_free(mem_ctx);
523
return ldb_next_init(module);
526
_PUBLIC_ const struct ldb_module_ops ldb_kludge_acl_module_ops = {
527
.name = "kludge_acl",
528
.search = kludge_acl_search,
529
.add = kludge_acl_change,
530
.modify = kludge_acl_change,
531
.del = kludge_acl_change,
532
.rename = kludge_acl_change,
533
.extended = kludge_acl_extended,
534
.init_context = kludge_acl_init