/* 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 3 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, see . */ /* * Name: ldb * * Component: ldb tdb cache functions * * Description: cache special records in a ldb/tdb * * Author: Andrew Tridgell */ #include "ldb_includes.h" #include "ldb_tdb.h" #define LTDB_FLAG_CASE_INSENSITIVE (1<<0) #define LTDB_FLAG_INTEGER (1<<1) #define LTDB_FLAG_HIDDEN (1<<2) /* 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 }, { "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 = (struct ltdb_private *)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;inum_elements;i++) { ldb_schema_attribute_remove(module->ldb, msg->elements[i].name); } 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;inum_values;i++) { int j; for (j=0;ltdb_valid_attr_flags[j].name;j++) { if (strcmp(ltdb_valid_attr_flags[j].name, (char *)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 = (struct ltdb_private *)module->private_data; struct ldb_message *msg = ltdb->cache->attributes; struct ldb_dn *dn; int i, r; dn = ldb_dn_new(module, module->ldb, LTDB_ATTRIBUTES); if (dn == NULL) goto failed; r = ltdb_search_dn1(module, dn, msg); talloc_free(dn); if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) { goto failed; } if (r == LDB_ERR_NO_SUCH_OBJECT) { return 0; } /* mapping these flags onto ldap 'syntaxes' isn't strictly correct, but its close enough for now */ for (i=0;inum_elements;i++) { unsigned flags; const char *syntax; const struct ldb_schema_syntax *s; 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_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; } s = ldb_standard_syntax_by_name(module->ldb, syntax); if (s == NULL) { ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Invalid attribute syntax '%s' for '%s' in @ATTRIBUTES\n", syntax, msg->elements[i].name); goto failed; } flags |= LDB_ATTR_FLAG_ALLOCATED; if (ldb_schema_attribute_add_with_syntax(module->ldb, msg->elements[i].name, flags, s) != 0) { goto failed; } } return 0; failed: return -1; } /* initialise the baseinfo record */ static int ltdb_baseinfo_init(struct ldb_module *module) { struct ltdb_private *ltdb = (struct ltdb_private *)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 = ⪙ msg->dn = ldb_dn_new(msg, module->ldb, 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 = (uint8_t *)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 LDB_ERR_OPERATIONS_ERROR; } /* free any cache records */ static void ltdb_cache_free(struct ldb_module *module) { struct ltdb_private *ltdb = (struct ltdb_private *)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_cache_free(module); return ltdb_cache_load(module); } /* load the cache records */ int ltdb_cache_load(struct ldb_module *module) { struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data; struct ldb_dn *baseinfo_dn = NULL, *options_dn = NULL; struct ldb_dn *indexlist_dn = NULL; uint64_t seq; struct ldb_message *baseinfo = NULL, *options = NULL; int r; /* a very fast check to avoid extra database reads */ if (ltdb->cache != NULL && tdb_get_seqnum(ltdb->tdb) == ltdb->tdb_seqnum) { return 0; } 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->attributes = talloc_zero(ltdb->cache, struct ldb_message); if (ltdb->cache->indexlist == NULL || ltdb->cache->attributes == NULL) { goto failed; } } baseinfo = talloc(ltdb->cache, struct ldb_message); if (baseinfo == NULL) goto failed; baseinfo_dn = ldb_dn_new(module, module->ldb, LTDB_BASEINFO); if (baseinfo_dn == NULL) goto failed; r= ltdb_search_dn1(module, baseinfo_dn, baseinfo); if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) { goto failed; } /* possibly initialise the baseinfo */ if (r == LDB_ERR_NO_SUCH_OBJECT) { if (ltdb_baseinfo_init(module) != LDB_SUCCESS) { goto failed; } if (ltdb_search_dn1(module, baseinfo_dn, baseinfo) != LDB_SUCCESS) { goto failed; } } ltdb->tdb_seqnum = tdb_get_seqnum(ltdb->tdb); /* 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_attr_as_uint64(baseinfo, LTDB_SEQUENCE_NUMBER, 0); if (seq == ltdb->sequence_number) { goto done; } ltdb->sequence_number = seq; /* Read an interpret database options */ options = talloc(ltdb->cache, struct ldb_message); if (options == NULL) goto failed; options_dn = ldb_dn_new(options, module->ldb, LTDB_OPTIONS); if (options_dn == NULL) goto failed; r= ltdb_search_dn1(module, options_dn, options); if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) { goto failed; } /* set flag for checking base DN on searches */ if (r == LDB_SUCCESS) { ltdb->check_base = ldb_msg_find_attr_as_bool(options, LTDB_CHECK_BASE, false); } else { ltdb->check_base = false; } talloc_free(ltdb->cache->last_attribute.name); memset(<db->cache->last_attribute, 0, sizeof(ltdb->cache->last_attribute)); ltdb_attributes_unload(module); talloc_free(ltdb->cache->indexlist); ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message); ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message); if (ltdb->cache->indexlist == NULL || ltdb->cache->attributes == NULL) { goto failed; } indexlist_dn = ldb_dn_new(module, module->ldb, LTDB_INDEXLIST); if (indexlist_dn == NULL) goto failed; r = ltdb_search_dn1(module, indexlist_dn, ltdb->cache->indexlist); if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) { goto failed; } if (ltdb_attributes_load(module) == -1) { goto failed; } done: talloc_free(options); talloc_free(baseinfo); talloc_free(baseinfo_dn); talloc_free(indexlist_dn); return 0; failed: talloc_free(options); talloc_free(baseinfo); talloc_free(baseinfo_dn); talloc_free(indexlist_dn); return -1; } /* increase the sequence number to indicate a database change */ int ltdb_increase_sequence_number(struct ldb_module *module) { struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data; struct ldb_message *msg; struct ldb_message_element el[2]; struct ldb_val val; struct ldb_val val_time; time_t t = time(NULL); char *s = NULL; int ret; msg = talloc(ltdb, struct ldb_message); if (msg == NULL) { errno = ENOMEM; return LDB_ERR_OPERATIONS_ERROR; } s = talloc_asprintf(msg, "%llu", ltdb->sequence_number+1); if (!s) { errno = ENOMEM; return LDB_ERR_OPERATIONS_ERROR; } msg->num_elements = ARRAY_SIZE(el); msg->elements = el; msg->dn = ldb_dn_new(msg, module->ldb, LTDB_BASEINFO); if (msg->dn == NULL) { talloc_free(msg); errno = ENOMEM; return LDB_ERR_OPERATIONS_ERROR; } el[0].name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER); if (el[0].name == NULL) { talloc_free(msg); errno = ENOMEM; return LDB_ERR_OPERATIONS_ERROR; } el[0].values = &val; el[0].num_values = 1; el[0].flags = LDB_FLAG_MOD_REPLACE; val.data = (uint8_t *)s; val.length = strlen(s); el[1].name = talloc_strdup(msg, LTDB_MOD_TIMESTAMP); if (el[1].name == NULL) { talloc_free(msg); errno = ENOMEM; return LDB_ERR_OPERATIONS_ERROR; } el[1].values = &val_time; el[1].num_values = 1; el[1].flags = LDB_FLAG_MOD_REPLACE; s = ldb_timestring(msg, t); if (s == NULL) { return LDB_ERR_OPERATIONS_ERROR; } val_time.data = (uint8_t *)s; val_time.length = strlen(s); ret = ltdb_modify_internal(module, msg); talloc_free(msg); if (ret == LDB_SUCCESS) { ltdb->sequence_number += 1; } /* updating the tdb_seqnum here avoids us reloading the cache records due to our own modification */ ltdb->tdb_seqnum = tdb_get_seqnum(ltdb->tdb); 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, (char *)value->data) == 0)) { return 0; } } return -1; }