1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-25 00:23:52 +03:00
Files
samba-mirror/source/lib/ldb/ldb_tdb/ldb_cache.c
Andrew Tridgell 944c5844ab r8037: a fairly major update to the internals of ldb. Changes are:
- moved the knowledge of attribute types out of ldb_tdb and into the
   generic ldb code. This allows the ldb_match() message match logic
   to be generic, so it can be used by other backend

 - added the generic ability to load attribute handlers, for
   canonicalisation, compare, ldif read and ldif write. In the future
   this will be used by the schema module to allow us to correctly
   obey the attributetype schema elements

 - added attribute handlers for some of the core ldap attribute types,
   Integer, DirectoryString, DN, ObjectClass etc

 - added automatic registration of attribute handlers for well-known
   attribute names 'cn', 'dc', 'dn', 'ou' and 'objectClass'

 - converted the objectSid special handlers for Samba to the new system

 - added more correct handling of indexing in tdb backend based on the
   attribute canonicalisation function

 - added generic support for subclasses, moving it out of the tdb
   backend. This will be used in future by the schema module

 - fixed several bugs in the dn_explode code. It still needs more
   work, but doesn't corrupt ldb dbs any more.
2007-10-10 13:19:01 -05:00

497 lines
12 KiB
C

/*
ldb database library
Copyright (C) Andrew Tridgell 2004
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
** under the LGPL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Name: ldb
*
* Component: ldb tdb cache functions
*
* Description: cache special records in a ldb/tdb
*
* Author: Andrew Tridgell
*/
#include "includes.h"
#include "ldb/include/ldb.h"
#include "ldb/include/ldb_private.h"
#include "ldb/ldb_tdb/ldb_tdb.h"
#define LTDB_FLAG_CASE_INSENSITIVE (1<<0)
#define LTDB_FLAG_INTEGER (1<<1)
#define LTDB_FLAG_WILDCARD (1<<2)
#define LTDB_FLAG_HIDDEN (1<<3)
#define LTDB_FLAG_OBJECTCLASS (1<<4)
/* valid attribute flags */
static const struct {
const char *name;
int value;
} ltdb_valid_attr_flags[] = {
{ "CASE_INSENSITIVE", LTDB_FLAG_CASE_INSENSITIVE },
{ "INTEGER", LTDB_FLAG_INTEGER },
{ "WILDCARD", LTDB_FLAG_WILDCARD },
{ "HIDDEN", LTDB_FLAG_HIDDEN },
{ "NONE", 0 },
{ NULL, 0 }
};
/*
de-register any special handlers for @ATTRIBUTES
*/
static void ltdb_attributes_unload(struct ldb_module *module)
{
struct ltdb_private *ltdb = module->private_data;
struct ldb_message *msg;
int i;
if (ltdb->cache->attributes == NULL) {
/* no previously loaded attributes */
return;
}
msg = ltdb->cache->attributes;
for (i=0;i<msg->num_elements;i++) {
const struct ldb_attrib_handler *h;
/* this is rather ugly - a consequence of const handling */
h = ldb_attrib_handler(module->ldb, msg->elements[i].name);
ldb_remove_attrib_handler(module->ldb, msg->elements[i].name);
if (strcmp(h->attr, msg->elements[i].name) == 0) {
talloc_steal(msg, h->attr);
}
}
talloc_free(ltdb->cache->attributes);
ltdb->cache->attributes = NULL;
}
/*
add up the attrib flags for a @ATTRIBUTES element
*/
static int ltdb_attributes_flags(struct ldb_message_element *el, unsigned *v)
{
int i;
unsigned value = 0;
for (i=0;i<el->num_values;i++) {
int j;
for (j=0;ltdb_valid_attr_flags[j].name;j++) {
if (strcmp(ltdb_valid_attr_flags[j].name,
el->values[i].data) == 0) {
value |= ltdb_valid_attr_flags[j].value;
break;
}
}
if (ltdb_valid_attr_flags[j].name == NULL) {
return -1;
}
}
*v = value;
return 0;
}
/*
register any special handlers from @ATTRIBUTES
*/
static int ltdb_attributes_load(struct ldb_module *module)
{
struct ltdb_private *ltdb = module->private_data;
struct ldb_message *msg = ltdb->cache->attributes;
int i;
if (ltdb_search_dn1(module, LTDB_ATTRIBUTES, msg) == -1) {
goto failed;
}
/* mapping these flags onto ldap 'syntaxes' isn't strictly correct,
but its close enough for now */
for (i=0;i<msg->num_elements;i++) {
unsigned flags;
const char *syntax;
const struct ldb_attrib_handler *h;
struct ldb_attrib_handler h2;
if (ltdb_attributes_flags(&msg->elements[i], &flags) != 0) {
ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Invalid @ATTRIBUTES element for '%s'\n", msg->elements[i].name);
goto failed;
}
switch (flags & ~LTDB_FLAG_HIDDEN) {
case 0:
syntax = LDB_SYNTAX_OCTET_STRING;
break;
case LTDB_FLAG_WILDCARD:
case LTDB_FLAG_WILDCARD | LTDB_FLAG_CASE_INSENSITIVE:
syntax = LDB_SYNTAX_WILDCARD;
break;
case LTDB_FLAG_CASE_INSENSITIVE:
syntax = LDB_SYNTAX_DIRECTORY_STRING;
break;
case LTDB_FLAG_INTEGER:
syntax = LDB_SYNTAX_INTEGER;
break;
default:
ldb_debug(module->ldb, LDB_DEBUG_ERROR,
"Invalid flag combination 0x%x for '%s' in @ATTRIBUTES\n",
flags, msg->elements[i].name);
goto failed;
}
h = ldb_attrib_handler_syntax(module->ldb, syntax);
if (h == NULL) {
ldb_debug(module->ldb, LDB_DEBUG_ERROR,
"Invalid attribute syntax '%s' for '%s' in @ATTRIBUTES\n",
syntax, msg->elements[i].name);
goto failed;
}
h2 = *h;
h2.attr = talloc_strdup(module, msg->elements[i].name);
if (ldb_set_attrib_handlers(module->ldb, &h2, 1) != 0) {
goto failed;
}
}
return 0;
failed:
return -1;
}
/*
register any subclasses from @SUBCLASSES
*/
static int ltdb_subclasses_load(struct ldb_module *module)
{
struct ltdb_private *ltdb = module->private_data;
struct ldb_message *msg = ltdb->cache->subclasses;
int i, j;
if (ltdb_search_dn1(module, LTDB_SUBCLASSES, msg) == -1) {
goto failed;
}
for (i=0;i<msg->num_elements;i++) {
struct ldb_message_element *el = &msg->elements[i];
for (j=0;j<el->num_values;j++) {
if (ldb_subclass_add(module->ldb, el->name, el->values[j].data) != 0) {
goto failed;
}
}
}
return 0;
failed:
return -1;
}
/*
de-register any @SUBCLASSES
*/
static void ltdb_subclasses_unload(struct ldb_module *module)
{
struct ltdb_private *ltdb = module->private_data;
struct ldb_message *msg;
int i;
if (ltdb->cache->subclasses == NULL) {
/* no previously loaded subclasses */
return;
}
msg = ltdb->cache->subclasses;
for (i=0;i<msg->num_elements;i++) {
ldb_subclass_remove(module->ldb, msg->elements[i].name);
}
talloc_free(ltdb->cache->subclasses);
ltdb->cache->subclasses = NULL;
}
/*
initialise the baseinfo record
*/
static int ltdb_baseinfo_init(struct ldb_module *module)
{
struct ltdb_private *ltdb = module->private_data;
struct ldb_message *msg;
struct ldb_message_element el;
struct ldb_val val;
int ret;
/* the initial sequence number must be different from the one
set in ltdb_cache_free(). Thanks to Jon for pointing this
out. */
const char *initial_sequence_number = "1";
ltdb->sequence_number = atof(initial_sequence_number);
msg = talloc(ltdb, struct ldb_message);
if (msg == NULL) {
goto failed;
}
msg->num_elements = 1;
msg->elements = &el;
msg->dn = talloc_strdup(msg, LTDB_BASEINFO);
if (!msg->dn) {
goto failed;
}
el.name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
if (!el.name) {
goto failed;
}
el.values = &val;
el.num_values = 1;
el.flags = 0;
val.data = talloc_strdup(msg, initial_sequence_number);
if (!val.data) {
goto failed;
}
val.length = 1;
ret = ltdb_store(module, msg, TDB_INSERT);
talloc_free(msg);
return ret;
failed:
talloc_free(msg);
errno = ENOMEM;
return -1;
}
/*
free any cache records
*/
static void ltdb_cache_free(struct ldb_module *module)
{
struct ltdb_private *ltdb = module->private_data;
ltdb->sequence_number = 0;
talloc_free(ltdb->cache);
ltdb->cache = NULL;
}
/*
force a cache reload
*/
int ltdb_cache_reload(struct ldb_module *module)
{
ltdb_attributes_unload(module);
ltdb_subclasses_unload(module);
ltdb_cache_free(module);
return ltdb_cache_load(module);
}
/*
load the cache records
*/
int ltdb_cache_load(struct ldb_module *module)
{
struct ltdb_private *ltdb = module->private_data;
double seq;
if (ltdb->cache == NULL) {
ltdb->cache = talloc_zero(ltdb, struct ltdb_cache);
if (ltdb->cache == NULL) goto failed;
ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message);
ltdb->cache->subclasses = talloc_zero(ltdb->cache, struct ldb_message);
ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message);
if (ltdb->cache->indexlist == NULL ||
ltdb->cache->subclasses == NULL ||
ltdb->cache->attributes == NULL) {
goto failed;
}
}
talloc_free(ltdb->cache->baseinfo);
ltdb->cache->baseinfo = talloc(ltdb->cache, struct ldb_message);
if (ltdb->cache->baseinfo == NULL) goto failed;
if (ltdb_search_dn1(module, LTDB_BASEINFO, ltdb->cache->baseinfo) == -1) {
goto failed;
}
/* possibly initialise the baseinfo */
if (!ltdb->cache->baseinfo->dn) {
if (ltdb_baseinfo_init(module) != 0) {
goto failed;
}
if (ltdb_search_dn1(module, LTDB_BASEINFO, ltdb->cache->baseinfo) != 1) {
goto failed;
}
}
/* if the current internal sequence number is the same as the one
in the database then assume the rest of the cache is OK */
seq = ldb_msg_find_double(ltdb->cache->baseinfo, LTDB_SEQUENCE_NUMBER, 0);
if (seq == ltdb->sequence_number) {
goto done;
}
ltdb->sequence_number = seq;
talloc_free(ltdb->cache->last_attribute.name);
memset(&ltdb->cache->last_attribute, 0, sizeof(ltdb->cache->last_attribute));
ltdb_attributes_unload(module);
ltdb_subclasses_unload(module);
talloc_free(ltdb->cache->indexlist);
talloc_free(ltdb->cache->subclasses);
ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message);
ltdb->cache->subclasses = talloc_zero(ltdb->cache, struct ldb_message);
ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message);
if (ltdb->cache->indexlist == NULL ||
ltdb->cache->subclasses == NULL ||
ltdb->cache->attributes == NULL) {
goto failed;
}
if (ltdb_search_dn1(module, LTDB_INDEXLIST, ltdb->cache->indexlist) == -1) {
goto failed;
}
if (ltdb_attributes_load(module) == -1) {
goto failed;
}
if (ltdb_subclasses_load(module) == -1) {
goto failed;
}
done:
return 0;
failed:
return -1;
}
/*
increase the sequence number to indicate a database change
*/
int ltdb_increase_sequence_number(struct ldb_module *module)
{
struct ltdb_private *ltdb = module->private_data;
struct ldb_message *msg;
struct ldb_message_element el;
struct ldb_val val;
char *s = NULL;
int ret;
msg = talloc(ltdb, struct ldb_message);
if (msg == NULL) {
errno = ENOMEM;
return -1;
}
s = talloc_asprintf(msg, "%.0f", ltdb->sequence_number+1);
if (!s) {
errno = ENOMEM;
return -1;
}
msg->num_elements = 1;
msg->elements = &el;
msg->dn = talloc_strdup(msg, LTDB_BASEINFO);
el.name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
el.values = &val;
el.num_values = 1;
el.flags = LDB_FLAG_MOD_REPLACE;
val.data = s;
val.length = strlen(s);
ret = ltdb_modify_internal(module, msg);
talloc_free(msg);
if (ret == 0) {
ltdb->sequence_number += 1;
}
return ret;
}
/*
return the attribute flags from the @ATTRIBUTES record
for the given attribute
*/
int ltdb_attribute_flags(struct ldb_module *module, const char *attr_name)
{
struct ltdb_private *ltdb = module->private_data;
const struct ldb_message_element *attr_el;
int i, j, ret=0;
if (ltdb->cache->last_attribute.name &&
ldb_attr_cmp(ltdb->cache->last_attribute.name, attr_name) == 0) {
return ltdb->cache->last_attribute.flags;
}
/* objectclass is a special default case */
if (ldb_attr_cmp(attr_name, LTDB_OBJECTCLASS) == 0) {
ret = LTDB_FLAG_OBJECTCLASS | LTDB_FLAG_CASE_INSENSITIVE;
}
attr_el = ldb_msg_find_element(ltdb->cache->attributes, attr_name);
if (!attr_el) {
/* check if theres a wildcard attribute */
attr_el = ldb_msg_find_element(ltdb->cache->attributes, "*");
if (!attr_el) {
return ret;
}
}
for (i = 0; i < attr_el->num_values; i++) {
for (j=0; ltdb_valid_attr_flags[j].name; j++) {
if (strcmp(ltdb_valid_attr_flags[j].name,
attr_el->values[i].data) == 0) {
ret |= ltdb_valid_attr_flags[j].value;
}
}
}
talloc_free(ltdb->cache->last_attribute.name);
ltdb->cache->last_attribute.name = talloc_strdup(ltdb->cache, attr_name);
ltdb->cache->last_attribute.flags = ret;
return ret;
}
int ltdb_check_at_attributes_values(const struct ldb_val *value)
{
int i;
for (i = 0; ltdb_valid_attr_flags[i].name != NULL; i++) {
if ((strcmp(ltdb_valid_attr_flags[i].name, value->data) == 0)) {
return 0;
}
}
return -1;
}