/*
ldb database library
Copyright (C) Simo Sorce 2006-2008
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
Copyright (C) Matthias Dieter Wallnöfer 2010-2011
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Name: ldb
*
* Component: objectClass sorting and constraint checking module
*
* Description:
* - sort the objectClass attribute into the class
* hierarchy and perform constraint checks (correct RDN name,
* valid parent),
* - fix DNs into 'standard' case
* - Add objectCategory and some other attribute defaults
*
* Author: Andrew Bartlett
*/
#include "includes.h"
#include "ldb_module.h"
#include "dsdb/samdb/samdb.h"
#include "librpc/ndr/libndr.h"
#include "librpc/gen_ndr/ndr_security.h"
#include "libcli/security/security.h"
#include "auth/auth.h"
#include "param/param.h"
#include "../libds/common/flags.h"
#include "dsdb/samdb/ldb_modules/util.h"
#undef strcasecmp
struct oc_context {
struct ldb_module *module;
struct ldb_request *req;
const struct dsdb_schema *schema;
struct ldb_reply *search_res;
struct ldb_reply *search_res2;
int (*step_fn)(struct oc_context *);
};
static struct oc_context *oc_init_context(struct ldb_module *module,
struct ldb_request *req)
{
struct ldb_context *ldb;
struct oc_context *ac;
ldb = ldb_module_get_ctx(module);
ac = talloc_zero(req, struct oc_context);
if (ac == NULL) {
ldb_oom(ldb);
return NULL;
}
ac->module = module;
ac->req = req;
ac->schema = dsdb_get_schema(ldb, ac);
return ac;
}
static int objectclass_do_add(struct oc_context *ac);
/*
* This checks if we have unrelated object classes in our entry's "objectClass"
* attribute. That means "unsatisfied" abstract classes (no concrete subclass)
* or two or more disjunct structural ones.
* If one of these conditions are true, blame.
*/
static int check_unrelated_objectclasses(struct ldb_module *module,
const struct dsdb_schema *schema,
const struct dsdb_class *struct_objectclass,
struct ldb_message_element *objectclass_element)
{
struct ldb_context *ldb = ldb_module_get_ctx(module);
unsigned int i;
bool found;
if (schema == NULL) {
return LDB_SUCCESS;
}
for (i = 0; i < objectclass_element->num_values; i++) {
const struct dsdb_class *tmp_class = dsdb_class_by_lDAPDisplayName_ldb_val(schema,
&objectclass_element->values[i]);
const struct dsdb_class *tmp_class2 = struct_objectclass;
/* Pointer comparison can be used due to the same schema str. */
if (tmp_class == NULL ||
tmp_class == struct_objectclass ||
tmp_class->objectClassCategory > 2 ||
ldb_attr_cmp(tmp_class->lDAPDisplayName, "top") == 0) {
continue;
}
found = false;
while (!found &&
ldb_attr_cmp(tmp_class2->lDAPDisplayName, "top") != 0) {
tmp_class2 = dsdb_class_by_lDAPDisplayName(schema,
tmp_class2->subClassOf);
if (tmp_class2 == tmp_class) {
found = true;
}
}
if (found) {
continue;
}
ldb_asprintf_errstring(ldb,
"objectclass: the objectclass '%s' seems to be unrelated to %s!",
tmp_class->lDAPDisplayName,
struct_objectclass->lDAPDisplayName);
return LDB_ERR_OBJECT_CLASS_VIOLATION;
}
return LDB_SUCCESS;
}
static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
{
struct ldb_context *ldb;
struct oc_context *ac;
int ret;
ac = talloc_get_type(req->context, struct oc_context);
ldb = ldb_module_get_ctx(ac->module);
if (!ares) {
return ldb_module_done(ac->req, NULL, NULL,
LDB_ERR_OPERATIONS_ERROR);
}
if (ares->error != LDB_SUCCESS &&
ares->error != LDB_ERR_NO_SUCH_OBJECT) {
return ldb_module_done(ac->req, ares->controls,
ares->response, ares->error);
}
ldb_reset_err_string(ldb);
switch (ares->type) {
case LDB_REPLY_ENTRY:
if (ac->search_res != NULL) {
ldb_set_errstring(ldb, "Too many results");
talloc_free(ares);
return ldb_module_done(ac->req, NULL, NULL,
LDB_ERR_OPERATIONS_ERROR);
}
ac->search_res = talloc_steal(ac, ares);
break;
case LDB_REPLY_REFERRAL:
/* ignore */
talloc_free(ares);
break;
case LDB_REPLY_DONE:
talloc_free(ares);
ret = ac->step_fn(ac);
if (ret != LDB_SUCCESS) {
return ldb_module_done(ac->req, NULL, NULL, ret);
}
break;
}
return LDB_SUCCESS;
}
/* Fix up the DN to be in the standard form, taking particular care to match the parent DN
This should mean that if the parent is:
CN=Users,DC=samba,DC=example,DC=com
and a proposed child is
cn=Admins ,cn=USERS,dc=Samba,dc=example,dc=COM
The resulting DN should be:
CN=Admins,CN=Users,DC=samba,DC=example,DC=com
*/
static int fix_dn(struct ldb_context *ldb,
TALLOC_CTX *mem_ctx,
struct ldb_dn *newdn, struct ldb_dn *parent_dn,
struct ldb_dn **fixed_dn)
{
char *upper_rdn_attr;
const struct ldb_val *rdn_val;
/* Fix up the DN to be in the standard form, taking particular care to
* match the parent DN */
*fixed_dn = ldb_dn_copy(mem_ctx, parent_dn);
if (*fixed_dn == NULL) {
return ldb_oom(ldb);
}
/* We need the attribute name in upper case */
upper_rdn_attr = strupper_talloc(*fixed_dn,
ldb_dn_get_rdn_name(newdn));
if (upper_rdn_attr == NULL) {
return ldb_oom(ldb);
}
/* Create a new child */
if (ldb_dn_add_child_fmt(*fixed_dn, "X=X") == false) {
return ldb_operr(ldb);
}
rdn_val = ldb_dn_get_rdn_val(newdn);
if (rdn_val == NULL) {
return ldb_operr(ldb);
}
#if 0
/* the rules for rDN length constraints are more complex than
this. Until we understand them we need to leave this
constraint out. Otherwise we break replication, as windows
does sometimes send us rDNs longer than 64 */
if (!rdn_val || rdn_val->length > 64) {
DEBUG(2,(__location__ ": WARNING: rDN longer than 64 limit for '%s'\n", ldb_dn_get_linearized(newdn)));
}
#endif
/* And replace it with CN=foo (we need the attribute in upper case) */
return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr, *rdn_val);
}
static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
{
struct ldb_context *ldb;
struct ldb_request *search_req;
struct oc_context *ac;
struct ldb_dn *parent_dn;
const struct ldb_val *val;
int ret;
static const char * const parent_attrs[] = { "objectClass", NULL };
ldb = ldb_module_get_ctx(module);
ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
/* do not manipulate our control entries */
if (ldb_dn_is_special(req->op.add.message->dn)) {
return ldb_next_request(module, req);
}
/* An add operation on the basedn without "NC-add" operation isn't
* allowed. */
if (ldb_dn_compare(ldb_get_default_basedn(ldb), req->op.add.message->dn) == 0) {
unsigned int instanceType;
instanceType = ldb_msg_find_attr_as_uint(req->op.add.message,
"instanceType", 0);
if (!(instanceType & INSTANCE_TYPE_IS_NC_HEAD)) {
char *referral_uri;
/* When we are trying to readd the root basedn then
* this is denied, but with an interesting mechanism:
* there is generated a referral with the last
* component value as hostname. */
val = ldb_dn_get_component_val(req->op.add.message->dn,
ldb_dn_get_comp_num(req->op.add.message->dn) - 1);
if (val == NULL) {
return ldb_operr(ldb);
}
referral_uri = talloc_asprintf(req, "ldap://%s/%s", val->data,
ldb_dn_get_linea
|