r4505: Add a first very basic schema module
[samba.git] / source4 / lib / ldb / modules / schema.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2004
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb schema module
29  *
30  *  Description: add schema check functionality
31  *
32  *  Author: Simo Sorce
33  */
34
35 #include "includes.h"
36 #include "ldb/include/ldb.h"
37 #include "ldb/include/ldb_private.h"
38
39 struct attribute_syntax {
40         const char *name;
41         const char *syntax_id;
42 };
43
44 static struct attribute_syntax attrsyn[] = {
45                 { "Object(DS-DN)", "2.5.5.1"},
46                 { "String(Object-Identifier)", "2.5.5.2"},
47                 { "", "2.5.5.3"},
48                 { "String(Teletex)", "2.5.5.4"},
49                 { "String(IA5)", "2.5.5.5"}, /* Also String(Printable) */
50                 { "String(Numeric)", "2.5.5.6"},
51                 { "Object(DN-Binary)", "2.5.5.7"}, /* Also Object(OR-Name) */
52                 { "Boolean", "2.5.5.8"},
53                 { "Integer", "2.5.5.9"}, /* Also Enumeration (3 types ?) ... */
54                 { "String(Octet)", "2.5.5.10"}, /* Also Object(Replica-Link) */
55                 { "String(UTC-Time)", "2.5.5.11"}, /* Also String(Generalized-Time) */
56                 { "String(Unicode)", "2.5.5.12"},
57                 { "Object(Presentation-Address)", "2.5.5.13"},
58                 { "Object(DN-String)", "2.5.5.14"}, /* Also Object(Access-Point) */
59                 { "String(NT-Sec-Desc))", "2.5.5.15"},
60                 { "LargeInteger", "2.5.5.16"}, /* Also Interval ... */
61                 { "String(Sid)", "2.5.5.17"}
62         };
63
64 #define SCHEMA_TALLOC_CHECK(root, mem, ret) do { if (!mem) { talloc_free(root); return ret;} } while(0);
65
66 struct private_data {
67         struct ldb_context *schema_db;
68         const char *error_string;
69 };
70
71 /* close */
72 static int schema_close(struct ldb_module *module)
73 {
74         return ldb_next_close(module);
75 }
76
77 /* search */
78 static int schema_search(struct ldb_module *module, const char *base,
79                        enum ldb_scope scope, const char *expression,
80                        const char * const *attrs, struct ldb_message ***res)
81 {
82         return ldb_next_search(module, base, scope, expression, attrs, res); 
83 }
84
85 /* search_free */
86 static int schema_search_free(struct ldb_module *module, struct ldb_message **res)
87 {
88         return ldb_next_search_free(module, res);
89 }
90
91 struct check_list {
92         int check;
93         char *name;
94 };
95
96 struct attr_list {
97         int syntax;
98         char *name;
99 };
100
101 struct objc_list {
102         int aux;
103         char *name;
104 };
105
106 struct schema_structures {
107         struct check_list *cl;
108         struct objc_list *ol;
109         struct attr_list *must;
110         struct attr_list *may;
111         int num_cl;
112         int num_objc;
113         int num_must;
114         int num_may;
115 };
116
117 /* add_record */
118 static int schema_add_record(struct ldb_module *module, const struct ldb_message *msg)
119 {
120         struct private_data *data = (struct private_data *)module->private_data;
121         struct ldb_message **srch;
122         struct schema_structures *ss;
123         int i, j, k, l;
124         int ret;
125
126         /* First implementation:
127                 Build up a list of must and mays from each objectclass
128                 Check all the musts are there and all the other attributes are mays
129                 Throw an error in case a check fail
130                 Free all structures and commit the change
131         */
132
133         ss = talloc_p(module, struct schema_structures);
134         if (!ss) {
135                 return -1;
136         }
137
138         ss->ol = NULL;
139         ss->num_cl = msg->num_elements;
140         ss->cl = talloc_array_p(ss, struct check_list, ss->num_cl);
141         SCHEMA_TALLOC_CHECK(ss, ss->cl, -1);
142         for (i = 0, j = 0; i < msg->num_elements; i++) {
143                 if (strcasecmp(msg->elements[i].name, "objectclass") == 0) {
144                         ss->num_objc = msg->elements[i].num_values;
145                         ss->ol = talloc_array_p(ss, struct objc_list, ss->num_objc);
146                         SCHEMA_TALLOC_CHECK(ss, ss->ol, -1);
147                         for (k = 0; k < ss->num_objc; k++) {
148                                 ss->ol[k].name = talloc_strndup(ss->ol, msg->elements[i].values[k].data, msg->elements[i].values[k].length);
149                                 SCHEMA_TALLOC_CHECK(ss, ss->ol[k].name, -1);
150                                 ss->ol[k].aux = 0;
151                         }
152                 }
153
154                 ss->cl[j].check = 0;
155                 ss->cl[j].name = talloc_strdup(ss->cl, msg->elements[i].name);
156                 SCHEMA_TALLOC_CHECK(ss, ss->cl[j].name, -1);
157                 j++;
158         }
159
160         /* find all other objectclasses recursively */
161         ss->must = NULL;
162         ss->may = NULL;
163         ss->num_must = 0;
164         ss->num_may = 0;
165         for (i = 0; i < ss->num_objc; i++) {
166                 char *filter;
167
168                 filter = talloc_asprintf(ss, "lDAPDisplayName=%s", ss->ol[i].name);
169                 SCHEMA_TALLOC_CHECK(ss, filter, -1);
170                 ret = ldb_search(data->schema_db, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &srch);
171                 if (ret == 0) {
172                         int ok;
173
174                         ok = 0;
175                         /* suppose auxiliary classess are not required */
176                         if (ss->ol[i].aux) {
177                                 int d;
178                                 ok = 1;
179                                 ss->num_objc -= 1;
180                                 for (d = i; d < ss->num_objc; d++) {
181                                         ss->ol[d] = ss->ol[d + 1];
182                                 }
183                                 i -= 1;
184                         }
185                         if (!ok) {
186                                 /* Schema Violation: Object Class Description Not Found */
187                                 data->error_string = "ObjectClass not found";
188                                 talloc_free(ss);
189                                 return -1;
190                         }
191                         continue;
192                 } else {
193                         if (ret < 0) {
194                                 /* Schema DB Error: Error occurred retrieving Object Class Description */
195                                 data->error_string = "Internal error. Error retrieving schema objectclass";
196                                 talloc_free(ss);
197                                 return -1;
198                         }
199                         if (ret > 1) {
200                                 /* Schema DB Error: Too Many Records */
201                                 data->error_string = "Internal error. Too many records searching for schema objectclass";
202                                 talloc_free(ss);
203                                 return -1;
204                         }
205                 }
206
207                 /* Add inherited classes eliminating duplicates */
208                 /* fill in kust and may attribute lists */
209                 for (j = 0; j < (*srch)->num_elements; j++) {
210                         int o, is_aux, is_class;
211
212                         is_aux = 0;
213                         is_class = 0;
214                         if (strcasecmp((*srch)->elements[j].name, "systemAuxiliaryclass") == 0) {
215                                 is_aux = 1;
216                                 is_class = 1;
217                         }
218                         if (strcasecmp((*srch)->elements[j].name, "subClassOf") == 0) {
219                                 is_class = 1;
220                         }
221
222                         if (is_class) {
223                                 o = (*srch)->elements[j].num_values;
224                                 ss->ol = talloc_realloc_p(ss, ss->ol, struct objc_list, ss->num_objc + o);
225                                 SCHEMA_TALLOC_CHECK(ss, ss->ol, -1);
226                                 for (k = 0, l = 0; k < o; k++) {
227                                         int c, found, len;
228
229                                         found = 0;
230                                         for (c = 0; c < ss->num_objc; c++) {
231                                                 len = strlen(ss->ol[c].name);
232                                                 if (len == (*srch)->elements[j].values[k].length) {
233                                                         if (strncasecmp(ss->ol[c].name, (*srch)->elements[j].values[k].data, len) == 0) {
234                                                                 found = 1;
235                                                                 break;
236                                                         }
237                                                 }
238                                         }
239                                         if (!found) {
240                                                 ss->ol[l + ss->num_objc].name = talloc_strndup(ss->ol, (*srch)->elements[j].values[k].data, (*srch)->elements[j].values[k].length);
241                                                 SCHEMA_TALLOC_CHECK(ss, ss->ol[l + ss->num_objc].name, -1);
242                                                 ss->ol[l + ss->num_objc].aux = is_aux;
243                                                 l++;
244                                         }
245                                 }
246                                 ss->num_objc += l;
247                         } else {
248
249                                 if (strcasecmp((*srch)->elements[j].name, "mustContain") == 0 || strcasecmp((*srch)->elements[j].name, "SystemMustContain") == 0) {
250                                         int m;
251
252                                         m = (*srch)->elements[j].num_values;
253
254                                         ss->must = talloc_realloc_p(ss, ss->must, struct attr_list, ss->num_must + m);
255                                         SCHEMA_TALLOC_CHECK(ss, ss->must, -1);
256                                         for (k = 0, l = 0; k < m; k++) {
257                                                 int c, found, len;
258
259                                                 found = 0;
260                                                 for (c = 0; c < ss->num_must; c++) {
261                                                         len  = strlen(ss->must[c].name);
262                                                         if (len == (*srch)->elements[j].values[k].length) {
263                                                                 if (strncasecmp(ss->must[c].name, (*srch)->elements[j].values[k].data, len) == 0) {
264                                                                         found = 1;
265                                                                         break;
266                                                                 }
267                                                         }
268                                                 }
269                                                 if (!found) {
270                                                         ss->must[l + ss->num_must].name = talloc_strndup(ss->must, (*srch)->elements[j].values[k].data, (*srch)->elements[j].values[k].length);
271                                                         SCHEMA_TALLOC_CHECK(ss, ss->must[l + ss->num_must].name, -1);
272                                                         l++;
273                                                 }
274                                         }
275                                         ss->num_must += l;
276                                 }
277
278                                 if (strcasecmp((*srch)->elements[j].name, "mayContain") == 0 || strcasecmp((*srch)->elements[j].name, "SystemMayContain") == 0) {
279                                         int m;
280
281                                         m = (*srch)->elements[j].num_values;
282
283                                         ss->may = talloc_realloc_p(ss, ss->may, struct attr_list, ss->num_may + m);
284                                         SCHEMA_TALLOC_CHECK(ss, ss->may, -1);
285                                         for (k = 0, l = 0; k < m; k++) {
286                                                 int c, found, len;
287
288                                                 found = 0;
289                                                 for (c = 0; c < ss->num_may; c++) {
290                                                         len = strlen(ss->may[c].name);
291                                                         if (len == (*srch)->elements[j].values[k].length) {
292                                                                 if (strncasecmp(ss->may[c].name, (*srch)->elements[j].values[k].data, (*srch)->elements[j].values[k].length) == 0) {
293                                                                         found = 1;
294                                                                         break;
295                                                                 }
296                                                         }
297                                                 }
298                                                 if (!found) {
299                                                         ss->may[l + ss->num_may].name = talloc_strndup(ss->may, (*srch)->elements[j].values[k].data, (*srch)->elements[j].values[k].length);
300                                                         SCHEMA_TALLOC_CHECK(ss, ss->may[l + ss->num_may].name, -1);
301                                                         l++;
302                                                 }
303                                         }
304                                         ss->num_may += l;
305                                 }
306                         }
307                 }
308
309                 ldb_search_free(module->ldb, srch);
310         }
311
312         /* now check all musts are present */
313         for (i = 0; i < ss->num_must; i++) {
314                 int found;
315
316                 found = 0;
317                 for (j = 0; j < ss->num_cl; j++) {
318                         if (strcasecmp(ss->must[i].name, ss->cl[j].name) == 0) {
319                                 ss->cl[j].check = 1;
320                                 found = 1;
321                                 break;
322                         }
323                 }
324
325                 if ( ! found ) {
326                         /* TODO: set the error string */
327                         data->error_string = "Objectclass violation, a required attribute is missing";
328                         talloc_free(ss);
329                         return -1;
330                 }
331         }
332
333         /* now check all others atribs are found in mays */
334         for (i = 0; i < ss->num_cl; i++) {
335
336                 if ( ! ss->cl[i].check ) {
337                         int found;
338
339                         found = 0;
340                         for (j = 0; j < ss->num_may; j++) {
341                                 if (strcasecmp(ss->may[j].name, ss->cl[i].name) == 0) {
342                                         ss->cl[i].check = 1;
343                                         found = 1;
344                                         break;
345                                 }
346                         }
347
348                         if ( ! found ) {
349                                 data->error_string = "Objectclass violation, an invalid attribute name was found";
350                                 talloc_free(ss);
351                                 return -1;
352                         }
353                 }
354         }
355
356         talloc_free(ss);
357
358         return ldb_next_add_record(module, msg);
359 }
360
361 /* modify_record */
362 static int schema_modify_record(struct ldb_module *module, const struct ldb_message *msg)
363 {
364         struct private_data *data = (struct private_data *)module->private_data;
365         return ldb_next_modify_record(module, msg);
366 }
367
368 /* delete_record */
369 static int schema_delete_record(struct ldb_module *module, const char *dn)
370 {
371         struct private_data *data = (struct private_data *)module->private_data;
372         return ldb_next_delete_record(module, dn);
373 }
374
375 /* rename_record */
376 static int schema_rename_record(struct ldb_module *module, const char *olddn, const char *newdn)
377 {
378         return ldb_next_rename_record(module, olddn, newdn);
379 }
380
381 static int schema_named_lock(struct ldb_module *module, const char *name) {
382         return ldb_next_named_lock(module, name);
383 }
384
385 static int schema_named_unlock(struct ldb_module *module, const char *name) {
386         return ldb_next_named_unlock(module, name);
387 }
388
389 /* return extended error information */
390 static const char *schema_errstring(struct ldb_module *module)
391 {
392         struct private_data *data = (struct private_data *)module->private_data;
393
394         if (data->error_string) {
395                 const char *error;
396
397                 error = data->error_string;
398                 data->error_string = NULL;
399                 return error;
400         }
401
402         return ldb_next_errstring(module);
403 }
404
405 static const struct ldb_module_ops schema_ops = {
406         "schema",
407         schema_close, 
408         schema_search,
409         schema_search_free,
410         schema_add_record,
411         schema_modify_record,
412         schema_delete_record,
413         schema_rename_record,
414         schema_named_lock,
415         schema_named_unlock,
416         schema_errstring,
417 };
418
419 #define SCHEMA_PREFIX           "schema:"
420 #define SCHEMA_PREFIX_LEN       7
421
422 #ifdef HAVE_DLOPEN_DISABLED
423 struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
424 #else
425 struct ldb_module *schema_module_init(struct ldb_context *ldb, const char *options[])
426 #endif
427 {
428         struct ldb_module *ctx;
429         struct private_data *data;
430         char *db_url = NULL;
431         int i;
432
433         ctx = talloc_p(ldb, struct ldb_module);
434         if (!ctx) {
435                 return NULL;
436         }
437
438         if (options) {
439                 for (i = 0; options[i] != NULL; i++) {
440                         if (strncmp(options[i], SCHEMA_PREFIX, SCHEMA_PREFIX_LEN) == 0) {
441                                 db_url = talloc_strdup(ctx, &options[i][SCHEMA_PREFIX_LEN]);
442                                 SCHEMA_TALLOC_CHECK(ctx, db_url, NULL);
443                         }
444                 }
445         }
446
447         if (!db_url) { /* search if it is defined in the calling ldb */
448                 int ret;
449                 const char * attrs[] = { "@SCHEMADB", NULL };
450                 struct ldb_message **msgs;
451
452                 ret = ldb_search(ldb, "", LDB_SCOPE_BASE, "dn=@MODULES", (const char * const *)attrs, &msgs);
453                 if (ret == 0) {
454                         ldb_debug(ldb, LDB_DEBUG_TRACE, "Schema DB not found\n");
455                         ldb_search_free(ldb, msgs);
456                         return NULL;
457                 } else {
458                         if (ret < 0) {
459                                 ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for schema db, bailing out!\n", ldb_errstring(ldb));
460                                 ldb_search_free(ldb, msgs);
461                                 return NULL;
462                         }
463                         if (ret > 1) {
464                                 ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found, bailing out\n");
465                                 ldb_search_free(ldb, msgs);
466                                 return NULL;
467                         }
468
469                         db_url = talloc_strndup(ctx, msgs[0]->elements[0].values[0].data, msgs[0]->elements[0].values[0].length);
470                         SCHEMA_TALLOC_CHECK(ctx, db_url, NULL);
471                 }
472
473                 ldb_search_free(ldb, msgs);
474         }
475
476         data = talloc_p(ctx, struct private_data);
477         SCHEMA_TALLOC_CHECK(ctx, data, NULL);
478
479         data->schema_db = ldb_connect(db_url, 0, NULL); 
480         SCHEMA_TALLOC_CHECK(ctx, data->schema_db, NULL);
481
482         data->error_string = NULL;
483         ctx->private_data = data;
484         ctx->ldb = ldb;
485         ctx->prev = ctx->next = NULL;
486         ctx->ops = &schema_ops;
487
488         return ctx;
489 }