4 Copyright (C) Andrew Bartlett 2005
5 Copyright (C) Simo Sorce 2006
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/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"
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");
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
;
78 int (*up_callback
)(struct ldb_context
*, void *, struct ldb_reply
*);
80 enum security_user_level user_type
;
81 bool allowedAttributes
;
82 bool allowedAttributesEffective
;
83 bool allowedChildClasses
;
84 bool allowedChildClassesEffective
;
88 /* read all objectClasses */
90 static int kludge_acl_allowedAttributes(struct ldb_context
*ldb
, struct ldb_message
*msg
,
93 struct ldb_message_element
*oc_el
;
94 struct ldb_message_element
*allowedAttributes
;
95 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
);
97 char **objectclass_list
, **attr_list
;
100 /* If we don't have a schema yet, we can't do anything... */
101 if (schema
== NULL
) {
105 /* Must remove any existing attribute, or else confusion reins */
106 ldb_msg_remove_attr(msg
, attrName
);
107 ret
= ldb_msg_add_empty(msg
, attrName
, 0, &allowedAttributes
);
108 if (ret
!= LDB_SUCCESS
) {
112 mem_ctx
= talloc_new(msg
);
115 return LDB_ERR_OPERATIONS_ERROR
;
118 /* To ensure that oc_el is valid, we must look for it after
119 we alter the element array in ldb_msg_add_empty() */
120 oc_el
= ldb_msg_find_element(msg
, "objectClass");
122 objectclass_list
= talloc_array(mem_ctx
, char *, oc_el
->num_values
+ 1);
123 if (!objectclass_list
) {
125 talloc_free(mem_ctx
);
126 return LDB_ERR_OPERATIONS_ERROR
;
129 for (i
=0; oc_el
&& i
< oc_el
->num_values
; i
++) {
130 objectclass_list
[i
] = (char *)oc_el
->values
[i
].data
;
132 objectclass_list
[i
] = NULL
;
134 attr_list
= dsdb_full_attribute_list(mem_ctx
, schema
, (const char **)objectclass_list
, DSDB_SCHEMA_ALL
);
136 ldb_asprintf_errstring(ldb
, "kludge_acl: Failed to get list of attributes create %s attribute", attrName
);
137 talloc_free(mem_ctx
);
138 return LDB_ERR_OPERATIONS_ERROR
;
141 for (i
=0; attr_list
&& attr_list
[i
]; i
++) {
142 ldb_msg_add_string(msg
, attrName
, attr_list
[i
]);
144 talloc_free(mem_ctx
);
148 /* read all objectClasses */
150 static int kludge_acl_childClasses(struct ldb_context
*ldb
, struct ldb_message
*msg
,
151 const char *attrName
)
153 struct ldb_message_element
*oc_el
;
154 struct ldb_message_element
*allowedClasses
;
155 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
);
156 const struct dsdb_class
*class;
159 /* If we don't have a schema yet, we can't do anything... */
160 if (schema
== NULL
) {
164 /* Must remove any existing attribute, or else confusion reins */
165 ldb_msg_remove_attr(msg
, attrName
);
166 ret
= ldb_msg_add_empty(msg
, attrName
, 0, &allowedClasses
);
167 if (ret
!= LDB_SUCCESS
) {
171 /* To ensure that oc_el is valid, we must look for it after
172 we alter the element array in ldb_msg_add_empty() */
173 oc_el
= ldb_msg_find_element(msg
, "objectClass");
175 for (i
=0; oc_el
&& i
< oc_el
->num_values
; i
++) {
176 class = dsdb_class_by_lDAPDisplayName(schema
, (const char *)oc_el
->values
[i
].data
);
178 /* We don't know this class? what is going on? */
182 for (j
=0; class->possibleInferiors
&& class->possibleInferiors
[j
]; j
++) {
183 ldb_msg_add_string(msg
, attrName
, class->possibleInferiors
[j
]);
187 if (allowedClasses
->num_values
> 1) {
188 qsort(allowedClasses
->values
,
189 allowedClasses
->num_values
,
190 sizeof(*allowedClasses
->values
),
191 (comparison_fn_t
)data_blob_cmp
);
193 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_context
*ldb
, void *context
, struct ldb_reply
*ares
)
212 struct kludge_acl_context
*ac
;
213 struct kludge_private_data
*data
;
216 ac
= talloc_get_type(context
, struct kludge_acl_context
);
217 data
= talloc_get_type(ac
->module
->private_data
, struct kludge_private_data
);
219 if (ares
->type
!= LDB_REPLY_ENTRY
) {
220 return ac
->up_callback(ldb
, ac
->up_context
, ares
);
223 if (ac
->allowedAttributes
) {
224 ret
= kludge_acl_allowedAttributes(ldb
, ares
->message
, "allowedAttributes");
225 if (ret
!= LDB_SUCCESS
) {
230 if (ac
->allowedChildClasses
) {
231 ret
= kludge_acl_childClasses(ldb
, ares
->message
, "allowedChildClasses");
232 if (ret
!= LDB_SUCCESS
) {
237 if (data
&& data
->password_attrs
) /* if we are not initialized just get through */
239 switch (ac
->user_type
) {
240 case SECURITY_SYSTEM
:
241 case SECURITY_ADMINISTRATOR
:
242 if (ac
->allowedAttributesEffective
) {
243 ret
= kludge_acl_allowedAttributes(ldb
, ares
->message
, "allowedAttributesEffective");
244 if (ret
!= LDB_SUCCESS
) {
248 if (ac
->allowedChildClassesEffective
) {
249 ret
= kludge_acl_childClasses(ldb
, ares
->message
, "allowedChildClassesEffective");
250 if (ret
!= LDB_SUCCESS
) {
256 /* remove password attributes */
257 for (i
= 0; data
->password_attrs
[i
]; i
++) {
258 ldb_msg_remove_attr(ares
->message
, data
->password_attrs
[i
]);
263 if ((ac
->allowedAttributes
|| ac
->allowedAttributesEffective
264 || ac
->allowedChildClasses
|| ac
->allowedChildClassesEffective
) &&
265 (!ldb_attr_in_list(ac
->attrs
, "objectClass") &&
266 !ldb_attr_in_list(ac
->attrs
, "*"))) {
267 ldb_msg_remove_attr(ares
->message
, "objectClass");
270 return ac
->up_callback(ldb
, ac
->up_context
, ares
);
273 static int kludge_acl_search(struct ldb_module
*module
, struct ldb_request
*req
)
275 struct kludge_acl_context
*ac
;
276 struct ldb_request
*down_req
;
277 struct kludge_private_data
*data
;
282 ac
= talloc(req
, struct kludge_acl_context
);
284 ldb_oom(module
->ldb
);
285 return LDB_ERR_OPERATIONS_ERROR
;
288 data
= talloc_get_type(module
->private_data
, struct kludge_private_data
);
291 ac
->up_context
= req
->context
;
292 ac
->up_callback
= req
->callback
;
293 ac
->user_type
= what_is_user(module
);
294 ac
->attrs
= req
->op
.search
.attrs
;
296 down_req
= talloc_zero(req
, struct ldb_request
);
297 if (down_req
== NULL
) {
298 ldb_oom(module
->ldb
);
299 return LDB_ERR_OPERATIONS_ERROR
;
302 down_req
->operation
= req
->operation
;
303 down_req
->op
.search
.base
= req
->op
.search
.base
;
304 down_req
->op
.search
.scope
= req
->op
.search
.scope
;
305 down_req
->op
.search
.tree
= req
->op
.search
.tree
;
306 down_req
->op
.search
.attrs
= req
->op
.search
.attrs
;
308 ac
->allowedAttributes
= ldb_attr_in_list(req
->op
.search
.attrs
, "allowedAttributes");
310 ac
->allowedAttributesEffective
= ldb_attr_in_list(req
->op
.search
.attrs
, "allowedAttributesEffective");
312 ac
->allowedChildClasses
= ldb_attr_in_list(req
->op
.search
.attrs
, "allowedChildClasses");
314 ac
->allowedChildClassesEffective
= ldb_attr_in_list(req
->op
.search
.attrs
, "allowedChildClassesEffective");
316 if (ac
->allowedAttributes
|| ac
->allowedAttributesEffective
|| ac
->allowedChildClasses
|| ac
->allowedChildClassesEffective
) {
317 down_req
->op
.search
.attrs
318 = ldb_attr_list_copy_add(down_req
, down_req
->op
.search
.attrs
, "objectClass");
321 /* FIXME: I hink we should copy the tree and keep the original
323 /* replace any attributes in the parse tree that are private,
324 so we don't allow a search for 'sambaPassword=penguin',
325 just as we would not allow that attribute to be returned */
326 switch (ac
->user_type
) {
327 case SECURITY_SYSTEM
:
330 /* remove password attributes */
331 for (i
= 0; data
&& data
->password_attrs
&& data
->password_attrs
[i
]; i
++) {
332 ldb_parse_tree_attr_replace(down_req
->op
.search
.tree
,
333 data
->password_attrs
[i
],
334 "kludgeACLredactedattribute");
338 down_req
->controls
= req
->controls
;
340 down_req
->context
= ac
;
341 down_req
->callback
= kludge_acl_callback
;
342 ldb_set_timeout_from_prev_req(module
->ldb
, req
, down_req
);
344 /* perform the search */
345 ret
= ldb_next_request(module
, down_req
);
347 /* do not free down_req as the call results may be linked to it,
348 * it will be freed when the upper level request get freed */
349 if (ret
== LDB_SUCCESS
) {
350 req
->handle
= down_req
->handle
;
356 /* ANY change type */
357 static int kludge_acl_change(struct ldb_module
*module
, struct ldb_request
*req
)
359 enum security_user_level user_type
= what_is_user(module
);
361 case SECURITY_SYSTEM
:
362 case SECURITY_ADMINISTRATOR
:
363 return ldb_next_request(module
, req
);
365 ldb_asprintf_errstring(module
->ldb
,
366 "kludge_acl_change: "
367 "attempted database modify not permitted. "
368 "User %s is not SYSTEM or an administrator",
369 user_name(req
, module
));
370 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS
;
374 static int kludge_acl_init(struct ldb_module
*module
)
377 TALLOC_CTX
*mem_ctx
= talloc_new(module
);
378 static const char *attrs
[] = { "passwordAttribute", NULL
};
379 struct ldb_result
*res
;
380 struct ldb_message
*msg
;
381 struct ldb_message_element
*password_attributes
;
383 struct kludge_private_data
*data
;
385 data
= talloc(module
, struct kludge_private_data
);
387 ldb_oom(module
->ldb
);
388 return LDB_ERR_OPERATIONS_ERROR
;
391 data
->password_attrs
= NULL
;
392 module
->private_data
= data
;
395 ldb_oom(module
->ldb
);
396 return LDB_ERR_OPERATIONS_ERROR
;
399 ret
= ldb_search(module
->ldb
, ldb_dn_new(mem_ctx
, module
->ldb
, "@KLUDGEACL"),
403 if (ret
!= LDB_SUCCESS
) {
406 talloc_steal(mem_ctx
, res
);
407 if (res
->count
== 0) {
411 if (res
->count
> 1) {
412 talloc_free(mem_ctx
);
413 return LDB_ERR_CONSTRAINT_VIOLATION
;
418 password_attributes
= ldb_msg_find_element(msg
, "passwordAttribute");
419 if (!password_attributes
) {
422 data
->password_attrs
= talloc_array(data
, const char *, password_attributes
->num_values
+ 1);
423 if (!data
->password_attrs
) {
424 talloc_free(mem_ctx
);
425 ldb_oom(module
->ldb
);
426 return LDB_ERR_OPERATIONS_ERROR
;
428 for (i
=0; i
< password_attributes
->num_values
; i
++) {
429 data
->password_attrs
[i
] = (const char *)password_attributes
->values
[i
].data
;
430 talloc_steal(data
->password_attrs
, password_attributes
->values
[i
].data
);
432 data
->password_attrs
[i
] = NULL
;
435 talloc_free(mem_ctx
);
436 return ldb_next_init(module
);
439 _PUBLIC_
const struct ldb_module_ops ldb_kludge_acl_module_ops
= {
440 .name
= "kludge_acl",
441 .search
= kludge_acl_search
,
442 .add
= kludge_acl_change
,
443 .modify
= kludge_acl_change
,
444 .del
= kludge_acl_change
,
445 .rename
= kludge_acl_change
,
446 .extended
= kludge_acl_change
,
447 .init_context
= kludge_acl_init