diff options
-rw-r--r-- | source4/lib/ldb/Makefile.ldb | 2 | ||||
-rw-r--r-- | source4/lib/ldb/common/ldb_modules.c | 4 | ||||
-rw-r--r-- | source4/lib/ldb/config.mk | 9 | ||||
-rw-r--r-- | source4/lib/ldb/modules/schema.c | 489 |
4 files changed, 501 insertions, 3 deletions
diff --git a/source4/lib/ldb/Makefile.ldb b/source4/lib/ldb/Makefile.ldb index 75c298241d..29d33007da 100644 --- a/source4/lib/ldb/Makefile.ldb +++ b/source4/lib/ldb/Makefile.ldb @@ -36,7 +36,7 @@ COMMON_OBJ=common/ldb.o common/ldb_ldif.o common/util.o \ common/ldb_parse.o common/ldb_msg.o common/ldb_utf8.o \ common/ldb_debug.o common/ldb_modules.o -MODULES_OBJ=modules/timestamps.o +MODULES_OBJ=modules/timestamps.o modules/schema.o OBJS = $(MODULES_OBJ) $(COMMON_OBJ) $(LDB_TDB_OBJ) $(TDB_OBJ) $(TALLOC_OBJ) $(LDB_LDAP_OBJ) diff --git a/source4/lib/ldb/common/ldb_modules.c b/source4/lib/ldb/common/ldb_modules.c index 4f53535bc4..033717a62b 100644 --- a/source4/lib/ldb/common/ldb_modules.c +++ b/source4/lib/ldb/common/ldb_modules.c @@ -130,7 +130,7 @@ int ldb_load_modules(struct ldb_context *ldb, const char *options[]) DLIST_ADD(ldb->modules, current); continue; } -#if 0 + if (strcmp(modules[i], "schema") == 0) { current = schema_module_init(ldb, options); if (!current) { @@ -140,7 +140,7 @@ int ldb_load_modules(struct ldb_context *ldb, const char *options[]) DLIST_ADD(ldb->modules, current); continue; } -#endif + #ifdef HAVE_DLOPEN_DISABLED { void *handle; diff --git a/source4/lib/ldb/config.mk b/source4/lib/ldb/config.mk index 8a7a34f05b..87ec89e5d3 100644 --- a/source4/lib/ldb/config.mk +++ b/source4/lib/ldb/config.mk @@ -8,6 +8,15 @@ INIT_OBJ_FILES = \ ################################################ ################################################ +# Start MODULE libldb_schema +[MODULE::libldb_schema] +SUBSYSTEM = LIBLDB +INIT_OBJ_FILES = \ + lib/ldb/modules/schema.o +# End MODULE libldb_schema +################################################ + +################################################ # Start MODULE libldb_ldap [MODULE::libldb_ldap] SUBSYSTEM = LIBLDB diff --git a/source4/lib/ldb/modules/schema.c b/source4/lib/ldb/modules/schema.c new file mode 100644 index 0000000000..f083eeb6e5 --- /dev/null +++ b/source4/lib/ldb/modules/schema.c @@ -0,0 +1,489 @@ +/* + ldb database library + + Copyright (C) Simo Sorce 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 schema module + * + * Description: add schema check functionality + * + * Author: Simo Sorce + */ + +#include "includes.h" +#include "ldb/include/ldb.h" +#include "ldb/include/ldb_private.h" + +struct attribute_syntax { + const char *name; + const char *syntax_id; +}; + +static struct attribute_syntax attrsyn[] = { + { "Object(DS-DN)", "2.5.5.1"}, + { "String(Object-Identifier)", "2.5.5.2"}, + { "", "2.5.5.3"}, + { "String(Teletex)", "2.5.5.4"}, + { "String(IA5)", "2.5.5.5"}, /* Also String(Printable) */ + { "String(Numeric)", "2.5.5.6"}, + { "Object(DN-Binary)", "2.5.5.7"}, /* Also Object(OR-Name) */ + { "Boolean", "2.5.5.8"}, + { "Integer", "2.5.5.9"}, /* Also Enumeration (3 types ?) ... */ + { "String(Octet)", "2.5.5.10"}, /* Also Object(Replica-Link) */ + { "String(UTC-Time)", "2.5.5.11"}, /* Also String(Generalized-Time) */ + { "String(Unicode)", "2.5.5.12"}, + { "Object(Presentation-Address)", "2.5.5.13"}, + { "Object(DN-String)", "2.5.5.14"}, /* Also Object(Access-Point) */ + { "String(NT-Sec-Desc))", "2.5.5.15"}, + { "LargeInteger", "2.5.5.16"}, /* Also Interval ... */ + { "String(Sid)", "2.5.5.17"} + }; + +#define SCHEMA_TALLOC_CHECK(root, mem, ret) do { if (!mem) { talloc_free(root); return ret;} } while(0); + +struct private_data { + struct ldb_context *schema_db; + const char *error_string; +}; + +/* close */ +static int schema_close(struct ldb_module *module) +{ + return ldb_next_close(module); +} + +/* search */ +static int schema_search(struct ldb_module *module, const char *base, + enum ldb_scope scope, const char *expression, + const char * const *attrs, struct ldb_message ***res) +{ + return ldb_next_search(module, base, scope, expression, attrs, res); +} + +/* search_free */ +static int schema_search_free(struct ldb_module *module, struct ldb_message **res) +{ + return ldb_next_search_free(module, res); +} + +struct check_list { + int check; + char *name; +}; + +struct attr_list { + int syntax; + char *name; +}; + +struct objc_list { + int aux; + char *name; +}; + +struct schema_structures { + struct check_list *cl; + struct objc_list *ol; + struct attr_list *must; + struct attr_list *may; + int num_cl; + int num_objc; + int num_must; + int num_may; +}; + +/* add_record */ +static int schema_add_record(struct ldb_module *module, const struct ldb_message *msg) +{ + struct private_data *data = (struct private_data *)module->private_data; + struct ldb_message **srch; + struct schema_structures *ss; + int i, j, k, l; + int ret; + + /* First implementation: + Build up a list of must and mays from each objectclass + Check all the musts are there and all the other attributes are mays + Throw an error in case a check fail + Free all structures and commit the change + */ + + ss = talloc_p(module, struct schema_structures); + if (!ss) { + return -1; + } + + ss->ol = NULL; + ss->num_cl = msg->num_elements; + ss->cl = talloc_array_p(ss, struct check_list, ss->num_cl); + SCHEMA_TALLOC_CHECK(ss, ss->cl, -1); + for (i = 0, j = 0; i < msg->num_elements; i++) { + if (strcasecmp(msg->elements[i].name, "objectclass") == 0) { + ss->num_objc = msg->elements[i].num_values; + ss->ol = talloc_array_p(ss, struct objc_list, ss->num_objc); + SCHEMA_TALLOC_CHECK(ss, ss->ol, -1); + for (k = 0; k < ss->num_objc; k++) { + ss->ol[k].name = talloc_strndup(ss->ol, msg->elements[i].values[k].data, msg->elements[i].values[k].length); + SCHEMA_TALLOC_CHECK(ss, ss->ol[k].name, -1); + ss->ol[k].aux = 0; + } + } + + ss->cl[j].check = 0; + ss->cl[j].name = talloc_strdup(ss->cl, msg->elements[i].name); + SCHEMA_TALLOC_CHECK(ss, ss->cl[j].name, -1); + j++; + } + + /* find all other objectclasses recursively */ + ss->must = NULL; + ss->may = NULL; + ss->num_must = 0; + ss->num_may = 0; + for (i = 0; i < ss->num_objc; i++) { + char *filter; + + filter = talloc_asprintf(ss, "lDAPDisplayName=%s", ss->ol[i].name); + SCHEMA_TALLOC_CHECK(ss, filter, -1); + ret = ldb_search(data->schema_db, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &srch); + if (ret == 0) { + int ok; + + ok = 0; + /* suppose auxiliary classess are not required */ + if (ss->ol[i].aux) { + int d; + ok = 1; + ss->num_objc -= 1; + for (d = i; d < ss->num_objc; d++) { + ss->ol[d] = ss->ol[d + 1]; + } + i -= 1; + } + if (!ok) { + /* Schema Violation: Object Class Description Not Found */ + data->error_string = "ObjectClass not found"; + talloc_free(ss); + return -1; + } + continue; + } else { + if (ret < 0) { + /* Schema DB Error: Error occurred retrieving Object Class Description */ + data->error_string = "Internal error. Error retrieving schema objectclass"; + talloc_free(ss); + return -1; + } + if (ret > 1) { + /* Schema DB Error: Too Many Records */ + data->error_string = "Internal error. Too many records searching for schema objectclass"; + talloc_free(ss); + return -1; + } + } + + /* Add inherited classes eliminating duplicates */ + /* fill in kust and may attribute lists */ + for (j = 0; j < (*srch)->num_elements; j++) { + int o, is_aux, is_class; + + is_aux = 0; + is_class = 0; + if (strcasecmp((*srch)->elements[j].name, "systemAuxiliaryclass") == 0) { + is_aux = 1; + is_class = 1; + } + if (strcasecmp((*srch)->elements[j].name, "subClassOf") == 0) { + is_class = 1; + } + + if (is_class) { + o = (*srch)->elements[j].num_values; + ss->ol = talloc_realloc_p(ss, ss->ol, struct objc_list, ss->num_objc + o); + SCHEMA_TALLOC_CHECK(ss, ss->ol, -1); + for (k = 0, l = 0; k < o; k++) { + int c, found, len; + + found = 0; + for (c = 0; c < ss->num_objc; c++) { + len = strlen(ss->ol[c].name); + if (len == (*srch)->elements[j].values[k].length) { + if (strncasecmp(ss->ol[c].name, (*srch)->elements[j].values[k].data, len) == 0) { + found = 1; + break; + } + } + } + if (!found) { + ss->ol[l + ss->num_objc].name = talloc_strndup(ss->ol, (*srch)->elements[j].values[k].data, (*srch)->elements[j].values[k].length); + SCHEMA_TALLOC_CHECK(ss, ss->ol[l + ss->num_objc].name, -1); + ss->ol[l + ss->num_objc].aux = is_aux; + l++; + } + } + ss->num_objc += l; + } else { + + if (strcasecmp((*srch)->elements[j].name, "mustContain") == 0 || strcasecmp((*srch)->elements[j].name, "SystemMustContain") == 0) { + int m; + + m = (*srch)->elements[j].num_values; + + ss->must = talloc_realloc_p(ss, ss->must, struct attr_list, ss->num_must + m); + SCHEMA_TALLOC_CHECK(ss, ss->must, -1); + for (k = 0, l = 0; k < m; k++) { + int c, found, len; + + found = 0; + for (c = 0; c < ss->num_must; c++) { + len = strlen(ss->must[c].name); + if (len == (*srch)->elements[j].values[k].length) { + if (strncasecmp(ss->must[c].name, (*srch)->elements[j].values[k].data, len) == 0) { + found = 1; + break; + } + } + } + if (!found) { + ss->must[l + ss->num_must].name = talloc_strndup(ss->must, (*srch)->elements[j].values[k].data, (*srch)->elements[j].values[k].length); + SCHEMA_TALLOC_CHECK(ss, ss->must[l + ss->num_must].name, -1); + l++; + } + } + ss->num_must += l; + } + + if (strcasecmp((*srch)->elements[j].name, "mayContain") == 0 || strcasecmp((*srch)->elements[j].name, "SystemMayContain") == 0) { + int m; + + m = (*srch)->elements[j].num_values; + + ss->may = talloc_realloc_p(ss, ss->may, struct attr_list, ss->num_may + m); + SCHEMA_TALLOC_CHECK(ss, ss->may, -1); + for (k = 0, l = 0; k < m; k++) { + int c, found, len; + + found = 0; + for (c = 0; c < ss->num_may; c++) { + len = strlen(ss->may[c].name); + if (len == (*srch)->elements[j].values[k].length) { + if (strncasecmp(ss->may[c].name, (*srch)->elements[j].values[k].data, (*srch)->elements[j].values[k].length) == 0) { + found = 1; + break; + } + } + } + if (!found) { + ss->may[l + ss->num_may].name = talloc_strndup(ss->may, (*srch)->elements[j].values[k].data, (*srch)->elements[j].values[k].length); + SCHEMA_TALLOC_CHECK(ss, ss->may[l + ss->num_may].name, -1); + l++; + } + } + ss->num_may += l; + } + } + } + + ldb_search_free(module->ldb, srch); + } + + /* now check all musts are present */ + for (i = 0; i < ss->num_must; i++) { + int found; + + found = 0; + for (j = 0; j < ss->num_cl; j++) { + if (strcasecmp(ss->must[i].name, ss->cl[j].name) == 0) { + ss->cl[j].check = 1; + found = 1; + break; + } + } + + if ( ! found ) { + /* TODO: set the error string */ + data->error_string = "Objectclass violation, a required attribute is missing"; + talloc_free(ss); + return -1; + } + } + + /* now check all others atribs are found in mays */ + for (i = 0; i < ss->num_cl; i++) { + + if ( ! ss->cl[i].check ) { + int found; + + found = 0; + for (j = 0; j < ss->num_may; j++) { + if (strcasecmp(ss->may[j].name, ss->cl[i].name) == 0) { + ss->cl[i].check = 1; + found = 1; + break; + } + } + + if ( ! found ) { + data->error_string = "Objectclass violation, an invalid attribute name was found"; + talloc_free(ss); + return -1; + } + } + } + + talloc_free(ss); + + return ldb_next_add_record(module, msg); +} + +/* modify_record */ +static int schema_modify_record(struct ldb_module *module, const struct ldb_message *msg) +{ + struct private_data *data = (struct private_data *)module->private_data; + return ldb_next_modify_record(module, msg); +} + +/* delete_record */ +static int schema_delete_record(struct ldb_module *module, const char *dn) +{ + struct private_data *data = (struct private_data *)module->private_data; + return ldb_next_delete_record(module, dn); +} + +/* rename_record */ +static int schema_rename_record(struct ldb_module *module, const char *olddn, const char *newdn) +{ + return ldb_next_rename_record(module, olddn, newdn); +} + +static int schema_named_lock(struct ldb_module *module, const char *name) { + return ldb_next_named_lock(module, name); +} + +static int schema_named_unlock(struct ldb_module *module, const char *name) { + return ldb_next_named_unlock(module, name); +} + +/* return extended error information */ +static const char *schema_errstring(struct ldb_module *module) +{ + struct private_data *data = (struct private_data *)module->private_data; + + if (data->error_string) { + const char *error; + + error = data->error_string; + data->error_string = NULL; + return error; + } + + return ldb_next_errstring(module); +} + +static const struct ldb_module_ops schema_ops = { + "schema", + schema_close, + schema_search, + schema_search_free, + schema_add_record, + schema_modify_record, + schema_delete_record, + schema_rename_record, + schema_named_lock, + schema_named_unlock, + schema_errstring, +}; + +#define SCHEMA_PREFIX "schema:" +#define SCHEMA_PREFIX_LEN 7 + +#ifdef HAVE_DLOPEN_DISABLED +struct ldb_module *init_module(struct ldb_context *ldb, const char *options[]) +#else +struct ldb_module *schema_module_init(struct ldb_context *ldb, const char *options[]) +#endif +{ + struct ldb_module *ctx; + struct private_data *data; + char *db_url = NULL; + int i; + + ctx = talloc_p(ldb, struct ldb_module); + if (!ctx) { + return NULL; + } + + if (options) { + for (i = 0; options[i] != NULL; i++) { + if (strncmp(options[i], SCHEMA_PREFIX, SCHEMA_PREFIX_LEN) == 0) { + db_url = talloc_strdup(ctx, &options[i][SCHEMA_PREFIX_LEN]); + SCHEMA_TALLOC_CHECK(ctx, db_url, NULL); + } + } + } + + if (!db_url) { /* search if it is defined in the calling ldb */ + int ret; + const char * attrs[] = { "@SCHEMADB", NULL }; + struct ldb_message **msgs; + + ret = ldb_search(ldb, "", LDB_SCOPE_BASE, "dn=@MODULES", (const char * const *)attrs, &msgs); + if (ret == 0) { + ldb_debug(ldb, LDB_DEBUG_TRACE, "Schema DB not found\n"); + ldb_search_free(ldb, msgs); + return NULL; + } else { + if (ret < 0) { + ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for schema db, bailing out!\n", ldb_errstring(ldb)); + ldb_search_free(ldb, msgs); + return NULL; + } + if (ret > 1) { + ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found, bailing out\n"); + ldb_search_free(ldb, msgs); + return NULL; + } + + db_url = talloc_strndup(ctx, msgs[0]->elements[0].values[0].data, msgs[0]->elements[0].values[0].length); + SCHEMA_TALLOC_CHECK(ctx, db_url, NULL); + } + + ldb_search_free(ldb, msgs); + } + + data = talloc_p(ctx, struct private_data); + SCHEMA_TALLOC_CHECK(ctx, data, NULL); + + data->schema_db = ldb_connect(db_url, 0, NULL); + SCHEMA_TALLOC_CHECK(ctx, data->schema_db, NULL); + + data->error_string = NULL; + ctx->private_data = data; + ctx->ldb = ldb; + ctx->prev = ctx->next = NULL; + ctx->ops = &schema_ops; + + return ctx; +} |