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"
38 #include "param/param.h"
39 #include "lib/util/tsort.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
;
54 static enum security_user_level
what_is_user(struct ldb_module
*module
)
56 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
57 struct auth_session_info
*session_info
58 = (struct auth_session_info
*)ldb_get_opaque(ldb
, "sessionInfo");
59 return security_session_user_level(session_info
);
62 static const char *user_name(TALLOC_CTX
*mem_ctx
, struct ldb_module
*module
)
64 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
65 struct auth_session_info
*session_info
66 = (struct auth_session_info
*)ldb_get_opaque(ldb
, "sessionInfo");
68 return "UNKNOWN (NULL)";
71 return talloc_asprintf(mem_ctx
, "%s\\%s",
72 session_info
->server_info
->domain_name
,
73 session_info
->server_info
->account_name
);
77 struct kludge_acl_context
{
79 struct ldb_module
*module
;
80 struct ldb_request
*req
;
82 enum security_user_level user_type
;
83 bool allowedAttributes
;
84 bool allowedAttributesEffective
;
85 bool allowedChildClasses
;
86 bool allowedChildClassesEffective
;
87 const char * const *attrs
;
90 /* read all objectClasses */
92 static int kludge_acl_allowedAttributes(struct ldb_context
*ldb
, struct ldb_message
*msg
,
95 struct ldb_message_element
*oc_el
;
96 struct ldb_message_element
*allowedAttributes
;
97 /* We need to ensure that the strings returned are valid for as long as the msg is valid */
98 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, msg
);
100 const char **attr_list
;
104 /* If we don't have a schema yet, we can't do anything... */
105 if (schema
== NULL
) {
109 /* Must remove any existing attribute, or else confusion reins */
110 ldb_msg_remove_attr(msg
, attrName
);
111 ret
= ldb_msg_add_empty(msg
, attrName
, 0, &allowedAttributes
);
112 if (ret
!= LDB_SUCCESS
) {
116 mem_ctx
= talloc_new(msg
);
119 return LDB_ERR_OPERATIONS_ERROR
;
122 /* To ensure that oc_el is valid, we must look for it after
123 we alter the element array in ldb_msg_add_empty() */
124 oc_el
= ldb_msg_find_element(msg
, "objectClass");
126 attr_list
= dsdb_full_attribute_list(mem_ctx
, schema
, oc_el
, DSDB_SCHEMA_ALL
);
128 ldb_asprintf_errstring(ldb
, "kludge_acl: Failed to get list of attributes create %s attribute", attrName
);
129 talloc_free(mem_ctx
);
130 return LDB_ERR_OPERATIONS_ERROR
;
133 for (i
=0; attr_list
&& attr_list
[i
]; i
++) {
134 ldb_msg_add_string(msg
, attrName
, attr_list
[i
]);
136 talloc_free(mem_ctx
);
140 /* read all objectClasses */
142 static int kludge_acl_childClasses(struct ldb_context
*ldb
, struct ldb_message
*msg
,
143 const char *attrName
)
145 struct ldb_message_element
*oc_el
;
146 struct ldb_message_element
*allowedClasses
;
148 /* We need to ensure that the strings returned are valid for as long as the msg is valid */
149 const struct dsdb_schema
*schema
= dsdb_get_schema(ldb
, msg
);
150 const struct dsdb_class
*sclass
;
154 /* If we don't have a schema yet, we can't do anything... */
155 if (schema
== NULL
) {
159 /* Must remove any existing attribute, or else confusion reins */
160 ldb_msg_remove_attr(msg
, attrName
);
161 ret
= ldb_msg_add_empty(msg
, attrName
, 0, &allowedClasses
);
162 if (ret
!= LDB_SUCCESS
) {
166 /* To ensure that oc_el is valid, we must look for it after
167 we alter the element array in ldb_msg_add_empty() */
168 oc_el
= ldb_msg_find_element(msg
, "objectClass");
170 for (i
=0; oc_el
&& i
< oc_el
->num_values
; i
++) {
171 sclass
= dsdb_class_by_lDAPDisplayName_ldb_val(schema
, &oc_el
->values
[i
]);
173 /* We don't know this class? what is going on? */
177 for (j
=0; sclass
->possibleInferiors
&& sclass
->possibleInferiors
[j
]; j
++) {
178 ldb_msg_add_string(msg
, attrName
, sclass
->possibleInferiors
[j
]);
182 if (allowedClasses
->num_values
> 1) {
183 TYPESAFE_QSORT(allowedClasses
->values
, allowedClasses
->num_values
, data_blob_cmp
);
185 for (i
=1 ; i
< allowedClasses
->num_values
; i
++) {
187 struct ldb_val
*val1
= &allowedClasses
->values
[i
-1];
188 struct ldb_val
*val2
= &allowedClasses
->values
[i
];
189 if (data_blob_cmp(val1
, val2
) == 0) {
190 memmove(val1
, val2
, (allowedClasses
->num_values
- i
) * sizeof( struct ldb_val
));
191 allowedClasses
->num_values
--;
200 /* find all attributes allowed by all these objectClasses */
202 static int kludge_acl_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
204 struct ldb_context
*ldb
;
205 struct kludge_acl_context
*ac
;
206 struct kludge_private_data
*data
;
210 ac
= talloc_get_type(req
->context
, struct kludge_acl_context
);
211 data
= talloc_get_type(ldb_module_get_private(ac
->module
), struct kludge_private_data
);
212 ldb
= ldb_module_get_ctx(ac
->module
);
215 return ldb_module_done(ac
->req
, NULL
, NULL
,
216 LDB_ERR_OPERATIONS_ERROR
);
218 if (ares
->error
!= LDB_SUCCESS
) {
219 return ldb_module_done(ac
->req
, ares
->controls
,
220 ares
->response
, ares
->error
);
223 switch (ares
->type
) {
224 case LDB_REPLY_ENTRY
:
225 if (ac
->allowedAttributes
) {
226 ret
= kludge_acl_allowedAttributes(ldb
,
228 "allowedAttributes");
229 if (ret
!= LDB_SUCCESS
) {
230 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
233 if (ac
->allowedChildClasses
) {
234 ret
= kludge_acl_childClasses(ldb
,
236 "allowedChildClasses");
237 if (ret
!= LDB_SUCCESS
) {
238 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
242 if (data
&& data
->password_attrs
) /* if we are not initialized just get through */
244 switch (ac
->user_type
) {
245 case SECURITY_SYSTEM
:
246 if (ac
->allowedAttributesEffective
) {
247 ret
= kludge_acl_allowedAttributes(ldb
, ares
->message
,
248 "allowedAttributesEffective");
249 if (ret
!= LDB_SUCCESS
) {
250 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
253 if (ac
->allowedChildClassesEffective
) {
254 ret
= kludge_acl_childClasses(ldb
, ares
->message
,
255 "allowedChildClassesEffective");
256 if (ret
!= LDB_SUCCESS
) {
257 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
262 case SECURITY_ADMINISTRATOR
:
263 if (ac
->allowedAttributesEffective
) {
264 ret
= kludge_acl_allowedAttributes(ldb
, ares
->message
,
265 "allowedAttributesEffective");
266 if (ret
!= LDB_SUCCESS
) {
267 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
270 if (ac
->allowedChildClassesEffective
) {
271 ret
= kludge_acl_childClasses(ldb
, ares
->message
,
272 "allowedChildClassesEffective");
273 if (ret
!= LDB_SUCCESS
) {
274 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
279 /* remove password attributes */
280 for (i
= 0; data
->password_attrs
[i
]; i
++) {
281 ldb_msg_remove_attr(ares
->message
, data
->password_attrs
[i
]);
286 if (ac
->allowedAttributes
||
287 ac
->allowedAttributesEffective
||
288 ac
->allowedChildClasses
||
289 ac
->allowedChildClassesEffective
) {
291 if (!ldb_attr_in_list(ac
->attrs
, "objectClass") &&
292 !ldb_attr_in_list(ac
->attrs
, "*")) {
294 ldb_msg_remove_attr(ares
->message
,
299 return ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
301 case LDB_REPLY_REFERRAL
:
302 return ldb_module_send_referral(ac
->req
, ares
->referral
);
305 return ldb_module_done(ac
->req
, ares
->controls
,
306 ares
->response
, LDB_SUCCESS
);
312 static int kludge_acl_search(struct ldb_module
*module
, struct ldb_request
*req
)
314 struct ldb_context
*ldb
;
315 struct kludge_acl_context
*ac
;
316 struct ldb_request
*down_req
;
317 struct kludge_private_data
*data
;
318 const char * const *attrs
;
322 ldb
= ldb_module_get_ctx(module
);
324 ac
= talloc(req
, struct kludge_acl_context
);
327 return LDB_ERR_OPERATIONS_ERROR
;
330 data
= talloc_get_type(ldb_module_get_private(module
), struct kludge_private_data
);
332 if (data
&& data
->acl_perform
)
333 return ldb_next_request(module
, req
);
337 ac
->user_type
= what_is_user(module
);
338 ac
->attrs
= req
->op
.search
.attrs
;
340 ac
->allowedAttributes
= ldb_attr_in_list(req
->op
.search
.attrs
, "allowedAttributes");
342 ac
->allowedAttributesEffective
= ldb_attr_in_list(req
->op
.search
.attrs
, "allowedAttributesEffective");
344 ac
->allowedChildClasses
= ldb_attr_in_list(req
->op
.search
.attrs
, "allowedChildClasses");
346 ac
->allowedChildClassesEffective
= ldb_attr_in_list(req
->op
.search
.attrs
, "allowedChildClassesEffective");
348 if (ac
->allowedAttributes
|| ac
->allowedAttributesEffective
|| ac
->allowedChildClasses
|| ac
->allowedChildClassesEffective
) {
349 attrs
= ldb_attr_list_copy_add(ac
, req
->op
.search
.attrs
, "objectClass");
351 attrs
= req
->op
.search
.attrs
;
354 /* replace any attributes in the parse tree that are private,
355 so we don't allow a search for 'userPassword=penguin',
356 just as we would not allow that attribute to be returned */
357 switch (ac
->user_type
) {
358 case SECURITY_SYSTEM
:
361 /* FIXME: We should copy the tree and keep the original unmodified. */
362 /* remove password attributes */
364 if (!data
|| !data
->password_attrs
) {
367 for (i
= 0; data
->password_attrs
[i
]; i
++) {
368 ldb_parse_tree_attr_replace(req
->op
.search
.tree
,
369 data
->password_attrs
[i
],
370 "kludgeACLredactedattribute");
374 ret
= ldb_build_search_req_ex(&down_req
,
377 req
->op
.search
.scope
,
381 ac
, kludge_acl_callback
,
383 if (ret
!= LDB_SUCCESS
) {
387 /* perform the search */
388 return ldb_next_request(module
, down_req
);
391 /* ANY change type */
392 static int kludge_acl_change(struct ldb_module
*module
, struct ldb_request
*req
)
394 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
395 enum security_user_level user_type
= what_is_user(module
);
396 struct kludge_private_data
*data
= talloc_get_type(ldb_module_get_private(module
),
397 struct kludge_private_data
);
399 if (data
->acl_perform
)
400 return ldb_next_request(module
, req
);
403 case SECURITY_SYSTEM
:
404 case SECURITY_ADMINISTRATOR
:
405 return ldb_next_request(module
, req
);
407 ldb_asprintf_errstring(ldb
,
408 "kludge_acl_change: "
409 "attempted database modify not permitted. "
410 "User %s is not SYSTEM or an administrator",
411 user_name(req
, module
));
412 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS
;
416 static int kludge_acl_extended(struct ldb_module
*module
, struct ldb_request
*req
)
418 struct ldb_context
*ldb
= ldb_module_get_ctx(module
);
419 enum security_user_level user_type
;
421 /* allow everybody to read the sequence number */
422 if (strcmp(req
->op
.extended
.oid
,
423 LDB_EXTENDED_SEQUENCE_NUMBER
) == 0) {
424 return ldb_next_request(module
, req
);
427 user_type
= what_is_user(module
);
430 case SECURITY_SYSTEM
:
431 case SECURITY_ADMINISTRATOR
:
432 return ldb_next_request(module
, req
);
434 ldb_asprintf_errstring(ldb
,
435 "kludge_acl_change: "
436 "attempted database modify not permitted. "
437 "User %s is not SYSTEM or an administrator",
438 user_name(req
, module
));
439 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS
;
443 static int kludge_acl_init(struct ldb_module
*module
)
445 struct ldb_context
*ldb
;
448 TALLOC_CTX
*mem_ctx
= talloc_new(module
);
449 static const char *attrs
[] = { "passwordAttribute", NULL
};
450 struct ldb_result
*res
;
451 struct ldb_message
*msg
;
452 struct ldb_message_element
*password_attributes
;
454 struct kludge_private_data
*data
;
456 ldb
= ldb_module_get_ctx(module
);
458 data
= talloc(module
, struct kludge_private_data
);
461 return LDB_ERR_OPERATIONS_ERROR
;
464 data
->password_attrs
= NULL
;
465 data
->acl_perform
= lp_parm_bool(ldb_get_opaque(ldb
, "loadparm"),
466 NULL
, "acl", "perform", false);
467 ldb_module_set_private(module
, data
);
471 return LDB_ERR_OPERATIONS_ERROR
;
474 ret
= ldb_search(ldb
, mem_ctx
, &res
,
475 ldb_dn_new(mem_ctx
, ldb
, "@KLUDGEACL"),
476 LDB_SCOPE_BASE
, attrs
, NULL
);
477 if (ret
!= LDB_SUCCESS
) {
480 if (res
->count
== 0) {
484 if (res
->count
> 1) {
485 talloc_free(mem_ctx
);
486 return LDB_ERR_CONSTRAINT_VIOLATION
;
491 password_attributes
= ldb_msg_find_element(msg
, "passwordAttribute");
492 if (!password_attributes
) {
495 data
->password_attrs
= talloc_array(data
, const char *, password_attributes
->num_values
+ 1);
496 if (!data
->password_attrs
) {
497 talloc_free(mem_ctx
);
499 return LDB_ERR_OPERATIONS_ERROR
;
501 for (i
=0; i
< password_attributes
->num_values
; i
++) {
502 data
->password_attrs
[i
] = (const char *)password_attributes
->values
[i
].data
;
503 talloc_steal(data
->password_attrs
, password_attributes
->values
[i
].data
);
505 data
->password_attrs
[i
] = NULL
;
508 talloc_free(mem_ctx
);
509 return ldb_next_init(module
);
512 _PUBLIC_
const struct ldb_module_ops ldb_kludge_acl_module_ops
= {
513 .name
= "kludge_acl",
514 /* .search = kludge_acl_search,
515 .add = kludge_acl_change,
516 .modify = kludge_acl_change,
517 .del = kludge_acl_change,
518 .rename = kludge_acl_change, */
519 .extended
= kludge_acl_extended
,
520 .init_context
= kludge_acl_init