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/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
;
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 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
, 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
] = (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
);
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
*class;
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 class = 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; 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
--;
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
;
216 ac
= talloc_get_type(req
->context
, struct kludge_acl_context
);
217 data
= talloc_get_type(ac
->module
->private_data
, struct kludge_private_data
);
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
,
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
,
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
);
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
);
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
,
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
);
310 return ldb_module_done(ac
->req
, ares
->controls
,
311 ares
->response
, 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
;
325 ac
= talloc(req
, struct kludge_acl_context
);
327 ldb_oom(module
->ldb
);
328 return LDB_ERR_OPERATIONS_ERROR
;
331 data
= talloc_get_type(module
->private_data
, struct kludge_private_data
);
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");
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
:
359 /* FIXME: We should copy the tree and keep the original unmodified. */
360 /* remove password attributes */
362 if (!data
|| !data
->password_attrs
) {
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
,
375 req
->op
.search
.scope
,
379 ac
, kludge_acl_callback
,
381 if (ret
!= LDB_SUCCESS
) {
382 return LDB_ERR_OPERATIONS_ERROR
;
385 /* perform the search */
386 return ldb_next_request(module
, down_req
);
389 /* ANY change type */
390 static int kludge_acl_change(struct ldb_module
*module
, struct ldb_request
*req
)
392 enum security_user_level user_type
= what_is_user(module
);
394 case SECURITY_SYSTEM
:
395 case SECURITY_ADMINISTRATOR
:
396 return ldb_next_request(module
, req
);
398 ldb_asprintf_errstring(module
->ldb
,
399 "kludge_acl_change: "
400 "attempted database modify not permitted. "
401 "User %s is not SYSTEM or an administrator",
402 user_name(req
, module
));
403 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS
;
407 static int kludge_acl_init(struct ldb_module
*module
)
410 TALLOC_CTX
*mem_ctx
= talloc_new(module
);
411 static const char *attrs
[] = { "passwordAttribute", NULL
};
412 struct ldb_result
*res
;
413 struct ldb_message
*msg
;
414 struct ldb_message_element
*password_attributes
;
416 struct kludge_private_data
*data
;
418 data
= talloc(module
, struct kludge_private_data
);
420 ldb_oom(module
->ldb
);
421 return LDB_ERR_OPERATIONS_ERROR
;
424 data
->password_attrs
= NULL
;
425 module
->private_data
= data
;
428 ldb_oom(module
->ldb
);
429 return LDB_ERR_OPERATIONS_ERROR
;
432 ret
= ldb_search(module
->ldb
, mem_ctx
, &res
,
433 ldb_dn_new(mem_ctx
, module
->ldb
, "@KLUDGEACL"),
434 LDB_SCOPE_BASE
, attrs
, NULL
);
435 if (ret
!= LDB_SUCCESS
) {
438 if (res
->count
== 0) {
442 if (res
->count
> 1) {
443 talloc_free(mem_ctx
);
444 return LDB_ERR_CONSTRAINT_VIOLATION
;
449 password_attributes
= ldb_msg_find_element(msg
, "passwordAttribute");
450 if (!password_attributes
) {
453 data
->password_attrs
= talloc_array(data
, const char *, password_attributes
->num_values
+ 1);
454 if (!data
->password_attrs
) {
455 talloc_free(mem_ctx
);
456 ldb_oom(module
->ldb
);
457 return LDB_ERR_OPERATIONS_ERROR
;
459 for (i
=0; i
< password_attributes
->num_values
; i
++) {
460 data
->password_attrs
[i
] = (const char *)password_attributes
->values
[i
].data
;
461 talloc_steal(data
->password_attrs
, password_attributes
->values
[i
].data
);
463 data
->password_attrs
[i
] = NULL
;
466 talloc_free(mem_ctx
);
467 return ldb_next_init(module
);
470 _PUBLIC_
const struct ldb_module_ops ldb_kludge_acl_module_ops
= {
471 .name
= "kludge_acl",
472 .search
= kludge_acl_search
,
473 .add
= kludge_acl_change
,
474 .modify
= kludge_acl_change
,
475 .del
= kludge_acl_change
,
476 .rename
= kludge_acl_change
,
477 .extended
= kludge_acl_change
,
478 .init_context
= kludge_acl_init