1 /* $OpenBSD: schema.c,v 1.20 2022/10/12 11:57:40 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2010 Martin Hedenfalk <martinh@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <syslog.h>
25
26 #include "ldapd.h"
27 #include "log.h"
28
29 #define ERROR -1
30 #define STRING 1
31
32 static int
attr_oid_cmp(struct attr_type * a,struct attr_type * b)33 attr_oid_cmp(struct attr_type *a, struct attr_type *b)
34 {
35 return strcasecmp(a->oid, b->oid);
36 }
37
38 static int
obj_oid_cmp(struct object * a,struct object * b)39 obj_oid_cmp(struct object *a, struct object *b)
40 {
41 return strcasecmp(a->oid, b->oid);
42 }
43
44 static int
oidname_cmp(struct oidname * a,struct oidname * b)45 oidname_cmp(struct oidname *a, struct oidname *b)
46 {
47 return strcasecmp(a->on_name, b->on_name);
48 }
49
50 static int
symoid_cmp(struct symoid * a,struct symoid * b)51 symoid_cmp(struct symoid *a, struct symoid *b)
52 {
53 return strcasecmp(a->name, b->name);
54 }
55
56 RB_GENERATE(attr_type_tree, attr_type, link, attr_oid_cmp);
57 RB_GENERATE(object_tree, object, link, obj_oid_cmp);
58 RB_GENERATE(oidname_tree, oidname, link, oidname_cmp);
59 RB_GENERATE(symoid_tree, symoid, link, symoid_cmp);
60
61 static struct attr_list *push_attr(struct attr_list *alist, struct attr_type *a);
62 static struct obj_list *push_obj(struct obj_list *olist, struct object *obj);
63 static struct name_list *push_name(struct name_list *nl, char *name);
64 int is_oidstr(const char *oidstr);
65
66 struct attr_type *
lookup_attribute_by_name(struct schema * schema,char * name)67 lookup_attribute_by_name(struct schema *schema, char *name)
68 {
69 struct oidname *on, find;
70
71 find.on_name = name;
72 on = RB_FIND(oidname_tree, &schema->attr_names, &find);
73
74 if (on)
75 return on->on_attr_type;
76 return NULL;
77 }
78
79 struct attr_type *
lookup_attribute_by_oid(struct schema * schema,char * oid)80 lookup_attribute_by_oid(struct schema *schema, char *oid)
81 {
82 struct attr_type find;
83
84 find.oid = oid;
85 return RB_FIND(attr_type_tree, &schema->attr_types, &find);
86 }
87
88 struct attr_type *
lookup_attribute(struct schema * schema,char * oid_or_name)89 lookup_attribute(struct schema *schema, char *oid_or_name)
90 {
91 if (is_oidstr(oid_or_name))
92 return lookup_attribute_by_oid(schema, oid_or_name);
93 return lookup_attribute_by_name(schema, oid_or_name);
94 }
95
96 struct object *
lookup_object_by_oid(struct schema * schema,char * oid)97 lookup_object_by_oid(struct schema *schema, char *oid)
98 {
99 struct object find;
100
101 find.oid = oid;
102 return RB_FIND(object_tree, &schema->objects, &find);
103 }
104
105 struct object *
lookup_object_by_name(struct schema * schema,char * name)106 lookup_object_by_name(struct schema *schema, char *name)
107 {
108 struct oidname *on, find;
109
110 find.on_name = name;
111 on = RB_FIND(oidname_tree, &schema->object_names, &find);
112
113 if (on)
114 return on->on_object;
115 return NULL;
116 }
117
118 struct object *
lookup_object(struct schema * schema,char * oid_or_name)119 lookup_object(struct schema *schema, char *oid_or_name)
120 {
121 if (is_oidstr(oid_or_name))
122 return lookup_object_by_oid(schema, oid_or_name);
123 return lookup_object_by_name(schema, oid_or_name);
124 }
125
126 /*
127 * Looks up a symbolic OID, optionally with a suffix OID, so if
128 * SYMBOL = 1.2.3.4
129 * then
130 * SYMBOL:5.6 = 1.2.3.4.5.6
131 *
132 * Returned string must be freed by the caller.
133 * Modifies the name argument.
134 */
135 char *
lookup_symbolic_oid(struct schema * schema,char * name)136 lookup_symbolic_oid(struct schema *schema, char *name)
137 {
138 struct symoid *symoid, find;
139 char *colon, *oid;
140 size_t sz;
141
142 colon = strchr(name, ':');
143 if (colon != NULL) {
144 if (!is_oidstr(colon + 1)) {
145 log_warnx("invalid OID after colon: %s", colon + 1);
146 return NULL;
147 }
148 *colon = '\0';
149 }
150
151 find.name = name;
152 symoid = RB_FIND(symoid_tree, &schema->symbolic_oids, &find);
153 if (symoid == NULL)
154 return NULL;
155
156 if (colon == NULL)
157 return strdup(symoid->oid);
158
159 /* Expand SYMBOL:OID. */
160 sz = strlen(symoid->oid) + 1 + strlen(colon + 1) + 1;
161 if ((oid = malloc(sz)) == NULL) {
162 log_warnx("malloc");
163 return NULL;
164 }
165
166 strlcpy(oid, symoid->oid, sz);
167 strlcat(oid, ".", sz);
168 strlcat(oid, colon + 1, sz);
169
170 return oid;
171 }
172
173 /*
174 * Push a symbol-OID pair on the tree. Name and OID must be valid pointers
175 * during the lifetime of the tree.
176 */
177 static struct symoid *
push_symbolic_oid(struct schema * schema,char * name,char * oid)178 push_symbolic_oid(struct schema *schema, char *name, char *oid)
179 {
180 struct symoid *symoid, find;
181
182 find.name = name;
183 symoid = RB_FIND(symoid_tree, &schema->symbolic_oids, &find);
184
185 if (symoid == NULL) {
186 symoid = calloc(1, sizeof(*symoid));
187 if (symoid == NULL) {
188 log_warnx("calloc");
189 return NULL;
190 }
191
192 symoid->name = name;
193 RB_INSERT(symoid_tree, &schema->symbolic_oids, symoid);
194 }
195
196 free(symoid->oid);
197 symoid->oid = oid;
198
199 return symoid;
200 }
201
202 static struct attr_list *
push_attr(struct attr_list * alist,struct attr_type * a)203 push_attr(struct attr_list *alist, struct attr_type *a)
204 {
205 struct attr_ptr *aptr;
206
207 if (alist == NULL) {
208 if ((alist = calloc(1, sizeof(*alist))) == NULL) {
209 log_warn("calloc");
210 return NULL;
211 }
212 SLIST_INIT(alist);
213 }
214
215 if ((aptr = calloc(1, sizeof(*aptr))) == NULL) {
216 log_warn("calloc");
217 free(alist);
218 return NULL;
219 }
220 aptr->attr_type = a;
221 SLIST_INSERT_HEAD(alist, aptr, next);
222
223 return alist;
224 }
225
226 static struct obj_list *
push_obj(struct obj_list * olist,struct object * obj)227 push_obj(struct obj_list *olist, struct object *obj)
228 {
229 struct obj_ptr *optr;
230
231 if (olist == NULL) {
232 if ((olist = calloc(1, sizeof(*olist))) == NULL) {
233 log_warn("calloc");
234 return NULL;
235 }
236 SLIST_INIT(olist);
237 }
238
239 if ((optr = calloc(1, sizeof(*optr))) == NULL) {
240 log_warn("calloc");
241 free(olist);
242 return NULL;
243 }
244 optr->object = obj;
245 SLIST_INSERT_HEAD(olist, optr, next);
246
247 return olist;
248 }
249
250 int
is_oidstr(const char * oidstr)251 is_oidstr(const char *oidstr)
252 {
253 struct ber_oid oid;
254 return (ober_string2oid(oidstr, &oid) == 0);
255 }
256
257 static struct name_list *
push_name(struct name_list * nl,char * name)258 push_name(struct name_list *nl, char *name)
259 {
260 struct name *n;
261
262 if (nl == NULL) {
263 if ((nl = calloc(1, sizeof(*nl))) == NULL) {
264 log_warn("calloc");
265 return NULL;
266 }
267 SLIST_INIT(nl);
268 }
269 if ((n = calloc(1, sizeof(*n))) == NULL) {
270 log_warn("calloc");
271 free(nl);
272 return NULL;
273 }
274 n->name = name;
275 SLIST_INSERT_HEAD(nl, n, next);
276
277 return nl;
278 }
279
280 static int
schema_getc(struct schema * schema,int quotec)281 schema_getc(struct schema *schema, int quotec)
282 {
283 int c, next;
284
285 if (schema->pushback_index)
286 return (schema->pushback_buffer[--schema->pushback_index]);
287
288 if (quotec) {
289 if ((c = getc(schema->fp)) == EOF) {
290 log_warnx("reached end of file while parsing "
291 "quoted string");
292 return EOF;
293 }
294 return (c);
295 }
296
297 while ((c = getc(schema->fp)) == '\\') {
298 next = getc(schema->fp);
299 if (next != '\n') {
300 c = next;
301 break;
302 }
303 schema->lineno++;
304 }
305
306 return (c);
307 }
308
309 static int
schema_ungetc(struct schema * schema,int c)310 schema_ungetc(struct schema *schema, int c)
311 {
312 if (c == EOF)
313 return EOF;
314
315 if (schema->pushback_index < SCHEMA_MAXPUSHBACK-1)
316 return (schema->pushback_buffer[schema->pushback_index++] = c);
317 else
318 return (EOF);
319 }
320
321 static int
findeol(struct schema * schema)322 findeol(struct schema *schema)
323 {
324 int c;
325
326 /* skip to either EOF or the first real EOL */
327 while (1) {
328 if (schema->pushback_index)
329 c = schema->pushback_buffer[--schema->pushback_index];
330 else
331 c = schema_getc(schema, 0);
332 if (c == '\n') {
333 schema->lineno++;
334 break;
335 }
336 if (c == EOF)
337 break;
338 }
339 return (ERROR);
340 }
341
342 static int
schema_lex(struct schema * schema,char ** kw)343 schema_lex(struct schema *schema, char **kw)
344 {
345 char buf[8096];
346 char *p;
347 int quotec, next, c;
348
349 if (kw)
350 *kw = NULL;
351
352 top:
353 p = buf;
354 while ((c = schema_getc(schema, 0)) == ' ' || c == '\t')
355 ; /* nothing */
356
357 if (c == '#')
358 while ((c = schema_getc(schema, 0)) != '\n' && c != EOF)
359 ; /* nothing */
360
361 switch (c) {
362 case '\'':
363 case '"':
364 quotec = c;
365 while (1) {
366 if ((c = schema_getc(schema, quotec)) == EOF)
367 return (0);
368 if (c == '\n') {
369 schema->lineno++;
370 continue;
371 } else if (c == '\\') {
372 if ((next = schema_getc(schema, quotec)) == EOF)
373 return (0);
374 if (next == quotec || c == ' ' || c == '\t')
375 c = next;
376 else if (next == '\n')
377 continue;
378 else
379 schema_ungetc(schema, next);
380 } else if (c == quotec) {
381 *p = '\0';
382 break;
383 }
384 if (p + 1 >= buf + sizeof(buf) - 1) {
385 log_warnx("string too long");
386 return (findeol(schema));
387 }
388 *p++ = (char)c;
389 }
390 if (kw != NULL && (*kw = strdup(buf)) == NULL)
391 fatal("schema_lex: strdup");
392 return (STRING);
393 }
394
395 #define allowed_in_string(x) \
396 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
397 x != '{' && x != '}' && x != '<' && x != '>' && \
398 x != '!' && x != '=' && x != '/' && x != '#' && \
399 x != ','))
400
401 if (isalnum(c) || c == ':' || c == '_' || c == '*') {
402 do {
403 *p++ = c;
404 if ((size_t)(p-buf) >= sizeof(buf)) {
405 log_warnx("string too long");
406 return (findeol(schema));
407 }
408 } while ((c = schema_getc(schema, 0)) != EOF && (allowed_in_string(c)));
409 schema_ungetc(schema, c);
410 *p = '\0';
411 if (kw != NULL && (*kw = strdup(buf)) == NULL)
412 fatal("schema_lex: strdup");
413 return STRING;
414 }
415 if (c == '\n') {
416 schema->lineno++;
417 goto top;
418 }
419 if (c == EOF)
420 return (0);
421 return (c);
422 }
423
424 struct schema *
schema_new(void)425 schema_new(void)
426 {
427 struct schema *schema;
428
429 if ((schema = calloc(1, sizeof(*schema))) == NULL)
430 return NULL;
431
432 RB_INIT(&schema->attr_types);
433 RB_INIT(&schema->attr_names);
434 RB_INIT(&schema->objects);
435 RB_INIT(&schema->object_names);
436 RB_INIT(&schema->symbolic_oids);
437
438 return schema;
439 }
440
441 static void
schema_err(struct schema * schema,const char * fmt,...)442 schema_err(struct schema *schema, const char *fmt, ...)
443 {
444 va_list ap;
445 char *msg;
446
447 va_start(ap, fmt);
448 if (vasprintf(&msg, fmt, ap) == -1)
449 fatal("vasprintf");
450 va_end(ap);
451 logit(LOG_CRIT, "%s:%d: %s", schema->filename, schema->lineno, msg);
452 free(msg);
453
454 schema->error++;
455 }
456
457 static int
schema_link_attr_name(struct schema * schema,const char * name,struct attr_type * attr)458 schema_link_attr_name(struct schema *schema, const char *name, struct attr_type *attr)
459 {
460 struct oidname *oidname, *prev;
461
462 if ((oidname = calloc(1, sizeof(*oidname))) == NULL) {
463 log_warn("calloc");
464 return -1;
465 }
466
467 oidname->on_name = name;
468 oidname->on_attr_type = attr;
469 prev = RB_INSERT(oidname_tree, &schema->attr_names, oidname);
470 if (prev != NULL) {
471 schema_err(schema, "attribute type name '%s'"
472 " already defined for oid %s",
473 name, prev->on_attr_type->oid);
474 free(oidname);
475 return -1;
476 }
477
478 return 0;
479 }
480
481 static int
schema_link_attr_names(struct schema * schema,struct attr_type * attr)482 schema_link_attr_names(struct schema *schema, struct attr_type *attr)
483 {
484 struct name *name;
485
486 SLIST_FOREACH(name, attr->names, next) {
487 if (schema_link_attr_name(schema, name->name, attr) != 0)
488 return -1;
489 }
490 return 0;
491 }
492
493 static int
schema_link_obj_name(struct schema * schema,const char * name,struct object * obj)494 schema_link_obj_name(struct schema *schema, const char *name, struct object *obj)
495 {
496 struct oidname *oidname, *prev;
497
498 if ((oidname = calloc(1, sizeof(*oidname))) == NULL) {
499 log_warn("calloc");
500 return -1;
501 }
502
503 oidname->on_name = name;
504 oidname->on_object = obj;
505 prev = RB_INSERT(oidname_tree, &schema->object_names, oidname);
506 if (prev != NULL) {
507 schema_err(schema, "object class name '%s'"
508 " already defined for oid %s",
509 name, prev->on_object->oid);
510 free(oidname);
511 return -1;
512 }
513
514 return 0;
515 }
516
517 static int
schema_link_obj_names(struct schema * schema,struct object * obj)518 schema_link_obj_names(struct schema *schema, struct object *obj)
519 {
520 struct name *name;
521
522 SLIST_FOREACH(name, obj->names, next) {
523 if (schema_link_obj_name(schema, name->name, obj) != 0)
524 return -1;
525 }
526 return 0;
527 }
528
529 static struct name_list *
schema_parse_names(struct schema * schema)530 schema_parse_names(struct schema *schema)
531 {
532 struct name_list *nlist = NULL;
533 char *kw;
534 int token;
535
536 token = schema_lex(schema, &kw);
537 if (token == STRING)
538 return push_name(NULL, kw);
539
540 if (token != '(')
541 goto fail;
542
543 for (;;) {
544 token = schema_lex(schema, &kw);
545 if (token == ')')
546 break;
547 if (token != STRING)
548 goto fail;
549 nlist = push_name(nlist, kw);
550 }
551
552 return nlist;
553
554 fail:
555 free(kw);
556 /* FIXME: leaks nlist here */
557 return NULL;
558 }
559
560 static void
schema_free_name_list(struct name_list * nlist)561 schema_free_name_list(struct name_list *nlist)
562 {
563 struct name *name;
564
565 while ((name = SLIST_FIRST(nlist)) != NULL) {
566 SLIST_REMOVE_HEAD(nlist, next);
567 free(name->name);
568 free(name);
569 }
570 free(nlist);
571 }
572
573 static struct attr_list *
schema_parse_attrlist(struct schema * schema)574 schema_parse_attrlist(struct schema *schema)
575 {
576 struct attr_list *alist = NULL;
577 struct attr_type *attr;
578 char *kw;
579 int token, want_dollar = 0;
580
581 token = schema_lex(schema, &kw);
582 if (token == STRING) {
583 if ((attr = lookup_attribute(schema, kw)) == NULL) {
584 schema_err(schema, "undeclared attribute type '%s'", kw);
585 goto fail;
586 }
587 free(kw);
588 return push_attr(NULL, attr);
589 }
590
591 if (token != '(')
592 goto fail;
593
594 for (;;) {
595 token = schema_lex(schema, &kw);
596 if (token == ')')
597 break;
598 if (token == '$') {
599 if (!want_dollar)
600 goto fail;
601 want_dollar = 0;
602 continue;
603 }
604 if (token != STRING)
605 goto fail;
606 if ((attr = lookup_attribute(schema, kw)) == NULL) {
607 schema_err(schema, "%s: no such attribute", kw);
608 goto fail;
609 }
610 alist = push_attr(alist, attr);
611 free(kw);
612 want_dollar = 1;
613 }
614
615 return alist;
616
617 fail:
618 free(kw);
619 /* FIXME: leaks alist here */
620 return NULL;
621 }
622
623 static struct obj_list *
schema_parse_objlist(struct schema * schema)624 schema_parse_objlist(struct schema *schema)
625 {
626 struct obj_list *olist = NULL;
627 struct object *obj;
628 char *kw;
629 int token, want_dollar = 0;
630
631 token = schema_lex(schema, &kw);
632 if (token == STRING) {
633 if ((obj = lookup_object(schema, kw)) == NULL) {
634 schema_err(schema, "undeclared object class '%s'", kw);
635 goto fail;
636 }
637 free(kw);
638 return push_obj(NULL, obj);
639 }
640
641 if (token != '(')
642 goto fail;
643
644 for (;;) {
645 token = schema_lex(schema, &kw);
646 if (token == ')')
647 break;
648 if (token == '$') {
649 if (!want_dollar)
650 goto fail;
651 want_dollar = 0;
652 continue;
653 }
654 if (token != STRING)
655 goto fail;
656 if ((obj = lookup_object(schema, kw)) == NULL)
657 goto fail;
658 olist = push_obj(olist, obj);
659 want_dollar = 1;
660 }
661
662 return olist;
663
664 fail:
665 free(kw);
666 /* FIXME: leaks olist here */
667 return NULL;
668 }
669
670 static int
schema_validate_match_rule(struct schema * schema,struct attr_type * at,const struct match_rule * mrule,enum match_rule_type type)671 schema_validate_match_rule(struct schema *schema, struct attr_type *at,
672 const struct match_rule *mrule, enum match_rule_type type)
673 {
674 int i;
675
676 if (mrule == NULL)
677 return 0;
678
679 if ((mrule->type & type) != type) {
680 schema_err(schema, "%s: bad matching rule '%s'",
681 ATTR_NAME(at), mrule->name);
682 return -1;
683 }
684
685 /* Is this matching rule compatible with the attribute syntax? */
686 if (strcmp(mrule->syntax_oid, at->syntax->oid) == 0)
687 return 0;
688
689 /* Check any alternative syntaxes for compatibility. */
690 for (i = 0; mrule->alt_syntax_oids && mrule->alt_syntax_oids[i]; i++)
691 if (strcmp(mrule->alt_syntax_oids[i], at->syntax->oid) == 0)
692 return 0;
693
694 schema_err(schema, "%s: inappropriate matching rule '%s' for syntax [%s]",
695 ATTR_NAME(at), mrule->name, at->syntax->oid);
696 return -1;
697 }
698
699 static int
schema_parse_attributetype(struct schema * schema)700 schema_parse_attributetype(struct schema *schema)
701 {
702 struct attr_type *attr = NULL, *prev, *sup;
703 struct name_list *xnames;
704 char *kw = NULL, *arg = NULL;
705 int token, ret = 0, c;
706
707 if (schema_lex(schema, NULL) != '(')
708 goto fail;
709
710 if (schema_lex(schema, &kw) != STRING)
711 goto fail;
712
713 if ((attr = calloc(1, sizeof(*attr))) == NULL) {
714 log_warn("calloc");
715 goto fail;
716 }
717 attr->usage = USAGE_USER_APP;
718
719 if (is_oidstr(kw))
720 attr->oid = kw;
721 else {
722 attr->oid = lookup_symbolic_oid(schema, kw);
723 if (attr->oid == NULL)
724 goto fail;
725 free(kw);
726 }
727 kw = NULL;
728
729 prev = RB_INSERT(attr_type_tree, &schema->attr_types, attr);
730 if (prev != NULL) {
731 schema_err(schema, "attribute type %s already defined", attr->oid);
732 goto fail;
733 }
734
735 while (ret == 0) {
736 token = schema_lex(schema, &kw);
737 if (token == ')')
738 break;
739 else if (token != STRING)
740 goto fail;
741 if (strcasecmp(kw, "NAME") == 0) {
742 attr->names = schema_parse_names(schema);
743 if (attr->names == NULL)
744 goto fail;
745 schema_link_attr_names(schema, attr);
746 } else if (strcasecmp(kw, "DESC") == 0) {
747 if (schema_lex(schema, &attr->desc) != STRING)
748 goto fail;
749 } else if (strcasecmp(kw, "OBSOLETE") == 0) {
750 attr->obsolete = 1;
751 } else if (strcasecmp(kw, "SUP") == 0) {
752 if (schema_lex(schema, &arg) != STRING)
753 goto fail;
754 if ((attr->sup = lookup_attribute(schema, arg)) == NULL) {
755 schema_err(schema, "%s: no such attribute", arg);
756 goto fail;
757 }
758 free(arg);
759 } else if (strcasecmp(kw, "EQUALITY") == 0) {
760 if (schema_lex(schema, &arg) != STRING)
761 goto fail;
762 if ((attr->equality = match_rule_lookup(arg)) == NULL) {
763 schema_err(schema, "%s: unknown matching rule",
764 arg);
765 goto fail;
766 }
767 free(arg);
768 } else if (strcasecmp(kw, "ORDERING") == 0) {
769 if (schema_lex(schema, &arg) != STRING)
770 goto fail;
771 if ((attr->ordering = match_rule_lookup(arg)) == NULL) {
772 schema_err(schema, "%s: unknown matching rule",
773 arg);
774 goto fail;
775 }
776 free(arg);
777 } else if (strcasecmp(kw, "SUBSTR") == 0) {
778 if (schema_lex(schema, &arg) != STRING)
779 goto fail;
780 if ((attr->substr = match_rule_lookup(arg)) == NULL) {
781 schema_err(schema, "%s: unknown matching rule",
782 arg);
783 goto fail;
784 }
785 free(arg);
786 } else if (strcasecmp(kw, "SYNTAX") == 0) {
787 if (schema_lex(schema, &arg) != STRING ||
788 !is_oidstr(arg))
789 goto fail;
790
791 if ((attr->syntax = syntax_lookup(arg)) == NULL) {
792 schema_err(schema, "syntax not supported: %s",
793 arg);
794 goto fail;
795 }
796
797 if ((c = schema_getc(schema, 0)) == '{') {
798 if (schema_lex(schema, NULL) != STRING ||
799 schema_lex(schema, NULL) != '}')
800 goto fail;
801 } else
802 schema_ungetc(schema, c);
803 free(arg);
804 } else if (strcasecmp(kw, "SINGLE-VALUE") == 0) {
805 attr->single = 1;
806 } else if (strcasecmp(kw, "COLLECTIVE") == 0) {
807 attr->collective = 1;
808 } else if (strcasecmp(kw, "NO-USER-MODIFICATION") == 0) {
809 attr->immutable = 1;
810 } else if (strcasecmp(kw, "USAGE") == 0) {
811 if (schema_lex(schema, &arg) != STRING)
812 goto fail;
813 if (strcasecmp(arg, "dSAOperation") == 0)
814 attr->usage = USAGE_DSA_OP;
815 else if (strcasecmp(arg, "directoryOperation") == 0)
816 attr->usage = USAGE_DIR_OP;
817 else if (strcasecmp(arg, "distributedOperation") == 0)
818 attr->usage = USAGE_DIST_OP;
819 else if (strcasecmp(arg, "userApplications") == 0)
820 attr->usage = USAGE_USER_APP;
821 else {
822 schema_err(schema, "invalid usage '%s'", arg);
823 goto fail;
824 }
825 free(arg);
826 } else if (strncmp(kw, "X-", 2) == 0) {
827 /* unknown extension, eat argument(s) */
828 xnames = schema_parse_names(schema);
829 if (xnames == NULL)
830 goto fail;
831 schema_free_name_list(xnames);
832 } else {
833 schema_err(schema, "syntax error at token '%s'", kw);
834 goto fail;
835 }
836 free(kw);
837 kw = NULL;
838 }
839
840 /* Check that a syntax is defined, either directly or
841 * indirectly via a superior attribute type.
842 */
843 sup = attr->sup;
844 while (attr->syntax == NULL && sup != NULL) {
845 attr->syntax = sup->syntax;
846 sup = sup->sup;
847 }
848 if (attr->syntax == NULL) {
849 schema_err(schema, "%s: no syntax defined", ATTR_NAME(attr));
850 goto fail;
851 }
852
853 /* If the attribute type doesn't explicitly define equality, check
854 * if any superior attribute type does.
855 */
856 sup = attr->sup;
857 while (attr->equality == NULL && sup != NULL) {
858 attr->equality = sup->equality;
859 sup = sup->sup;
860 }
861 /* Same thing with ordering matching rule. */
862 sup = attr->sup;
863 while (attr->ordering == NULL && sup != NULL) {
864 attr->ordering = sup->ordering;
865 sup = sup->sup;
866 }
867 /* ...and substring matching rule. */
868 sup = attr->sup;
869 while (attr->substr == NULL && sup != NULL) {
870 attr->substr = sup->substr;
871 sup = sup->sup;
872 }
873
874 if (schema_validate_match_rule(schema, attr, attr->equality, MATCH_EQUALITY) != 0 ||
875 schema_validate_match_rule(schema, attr, attr->ordering, MATCH_ORDERING) != 0 ||
876 schema_validate_match_rule(schema, attr, attr->substr, MATCH_SUBSTR) != 0)
877 goto fail;
878
879 return 0;
880
881 fail:
882 free(kw);
883 if (attr != NULL) {
884 if (attr->oid != NULL) {
885 RB_REMOVE(attr_type_tree, &schema->attr_types, attr);
886 free(attr->oid);
887 }
888 free(attr->desc);
889 free(attr);
890 }
891 return -1;
892 }
893
894 static int
schema_parse_objectclass(struct schema * schema)895 schema_parse_objectclass(struct schema *schema)
896 {
897 struct object *obj = NULL, *prev;
898 struct obj_ptr *optr;
899 struct name_list *xnames;
900 char *kw = NULL;
901 int token, ret = 0;
902
903 if (schema_lex(schema, NULL) != '(')
904 goto fail;
905
906 if (schema_lex(schema, &kw) != STRING)
907 goto fail;
908
909 if ((obj = calloc(1, sizeof(*obj))) == NULL) {
910 log_warn("calloc");
911 goto fail;
912 }
913 obj->kind = KIND_STRUCTURAL;
914
915 if (is_oidstr(kw))
916 obj->oid = kw;
917 else {
918 obj->oid = lookup_symbolic_oid(schema, kw);
919 if (obj->oid == NULL)
920 goto fail;
921 free(kw);
922 }
923 kw = NULL;
924
925 prev = RB_INSERT(object_tree, &schema->objects, obj);
926 if (prev != NULL) {
927 schema_err(schema, "object class %s already defined", obj->oid);
928 goto fail;
929 }
930
931 while (ret == 0) {
932 token = schema_lex(schema, &kw);
933 if (token == ')')
934 break;
935 else if (token != STRING)
936 goto fail;
937 if (strcasecmp(kw, "NAME") == 0) {
938 obj->names = schema_parse_names(schema);
939 if (obj->names == NULL)
940 goto fail;
941 schema_link_obj_names(schema, obj);
942 } else if (strcasecmp(kw, "DESC") == 0) {
943 if (schema_lex(schema, &obj->desc) != STRING)
944 goto fail;
945 } else if (strcasecmp(kw, "OBSOLETE") == 0) {
946 obj->obsolete = 1;
947 } else if (strcasecmp(kw, "SUP") == 0) {
948 obj->sup = schema_parse_objlist(schema);
949 if (obj->sup == NULL)
950 goto fail;
951 } else if (strcasecmp(kw, "ABSTRACT") == 0) {
952 obj->kind = KIND_ABSTRACT;
953 } else if (strcasecmp(kw, "STRUCTURAL") == 0) {
954 obj->kind = KIND_STRUCTURAL;
955 } else if (strcasecmp(kw, "AUXILIARY") == 0) {
956 obj->kind = KIND_AUXILIARY;
957 } else if (strcasecmp(kw, "MUST") == 0) {
958 obj->must = schema_parse_attrlist(schema);
959 if (obj->must == NULL)
960 goto fail;
961 } else if (strcasecmp(kw, "MAY") == 0) {
962 obj->may = schema_parse_attrlist(schema);
963 if (obj->may == NULL)
964 goto fail;
965 } else if (strncasecmp(kw, "X-", 2) == 0) {
966 /* unknown extension, eat argument(s) */
967 xnames = schema_parse_names(schema);
968 if (xnames == NULL)
969 goto fail;
970 schema_free_name_list(xnames);
971 } else {
972 schema_err(schema, "syntax error at token '%s'", kw);
973 goto fail;
974 }
975 free(kw);
976 kw = NULL;
977 }
978
979 /* Verify the subclassing is allowed.
980 *
981 * Structural object classes cannot subclass auxiliary object classes.
982 * Auxiliary object classes cannot subclass structural object classes.
983 * Abstract object classes cannot derive from structural or auxiliary
984 * object classes.
985 */
986 if (obj->sup != NULL) {
987 SLIST_FOREACH(optr, obj->sup, next) {
988 if (obj->kind == KIND_STRUCTURAL &&
989 optr->object->kind == KIND_AUXILIARY) {
990 log_warnx("structural object class '%s' cannot"
991 " subclass auxiliary object class '%s'",
992 OBJ_NAME(obj), OBJ_NAME(optr->object));
993 goto fail;
994 }
995
996 if (obj->kind == KIND_AUXILIARY &&
997 optr->object->kind == KIND_STRUCTURAL) {
998 log_warnx("auxiliary object class '%s' cannot"
999 " subclass structural object class '%s'",
1000 OBJ_NAME(obj), OBJ_NAME(optr->object));
1001 goto fail;
1002 }
1003
1004 if (obj->kind == KIND_ABSTRACT &&
1005 optr->object->kind != KIND_ABSTRACT) {
1006 log_warnx("abstract object class '%s' cannot"
1007 " subclass non-abstract object class '%s'",
1008 OBJ_NAME(obj), OBJ_NAME(optr->object));
1009 goto fail;
1010 }
1011 }
1012 }
1013
1014 return 0;
1015
1016 fail:
1017 free(kw);
1018 if (obj != NULL) {
1019 if (obj->oid != NULL) {
1020 RB_REMOVE(object_tree, &schema->objects, obj);
1021 free(obj->oid);
1022 }
1023 free(obj->desc);
1024 free(obj);
1025 }
1026 return -1;
1027 }
1028
1029 static int
schema_parse_objectidentifier(struct schema * schema)1030 schema_parse_objectidentifier(struct schema *schema)
1031 {
1032 char *symname = NULL, *symoid = NULL;
1033 char *oid = NULL;
1034
1035 if (schema_lex(schema, &symname) != STRING)
1036 goto fail;
1037 if (schema_lex(schema, &symoid) != STRING)
1038 goto fail;
1039
1040 if (is_oidstr(symoid)) {
1041 oid = symoid;
1042 symoid = NULL;
1043 } else if ((oid = lookup_symbolic_oid(schema, symoid)) == NULL)
1044 goto fail;
1045
1046 if (push_symbolic_oid(schema, symname, oid) == NULL)
1047 goto fail;
1048
1049 free(symoid);
1050 return 0;
1051
1052 fail:
1053 free(symname);
1054 free(symoid);
1055 free(oid);
1056 return -1;
1057 }
1058
1059 int
schema_parse(struct schema * schema,const char * filename)1060 schema_parse(struct schema *schema, const char *filename)
1061 {
1062 char *kw;
1063 int token, ret = 0;
1064
1065 log_debug("parsing schema file '%s'", filename);
1066
1067 if ((schema->fp = fopen(filename, "r")) == NULL) {
1068 log_warn("%s", filename);
1069 return -1;
1070 }
1071 schema->filename = filename;
1072 schema->lineno = 1;
1073
1074 while (ret == 0) {
1075 token = schema_lex(schema, &kw);
1076 if (token == STRING) {
1077 if (strcasecmp(kw, "attributetype") == 0)
1078 ret = schema_parse_attributetype(schema);
1079 else if (strcasecmp(kw, "objectclass") == 0)
1080 ret = schema_parse_objectclass(schema);
1081 else if (strcasecmp(kw, "objectidentifier") == 0)
1082 ret = schema_parse_objectidentifier(schema);
1083 else {
1084 schema_err(schema, "syntax error at '%s'", kw);
1085 ret = -1;
1086 }
1087 if (ret == -1 && schema->error == 0)
1088 schema_err(schema, "syntax error");
1089 free(kw);
1090 } else if (token == 0) { /* EOF */
1091 break;
1092 } else {
1093 schema_err(schema, "syntax error");
1094 ret = -1;
1095 }
1096 }
1097
1098 fclose(schema->fp);
1099 schema->fp = NULL;
1100 schema->filename = NULL;
1101
1102 return ret;
1103 }
1104
1105 static int
schema_dump_names(const char * desc,struct name_list * nlist,char * buf,size_t size)1106 schema_dump_names(const char *desc, struct name_list *nlist,
1107 char *buf, size_t size)
1108 {
1109 struct name *name;
1110
1111 if (nlist == NULL || SLIST_EMPTY(nlist))
1112 return 0;
1113
1114 if (strlcat(buf, " ", size) >= size ||
1115 strlcat(buf, desc, size) >= size)
1116 return -1;
1117
1118 name = SLIST_FIRST(nlist);
1119 if (SLIST_NEXT(name, next) == NULL) {
1120 /* single name, no parenthesis */
1121 if (strlcat(buf, " '", size) >= size ||
1122 strlcat(buf, name->name, size) >= size ||
1123 strlcat(buf, "'", size) >= size)
1124 return -1;
1125 } else {
1126 if (strlcat(buf, " ( ", size) >= size)
1127 return -1;
1128 SLIST_FOREACH(name, nlist, next)
1129 if (strlcat(buf, "'", size) >= size ||
1130 strlcat(buf, name->name, size) >= size ||
1131 strlcat(buf, "' ", size) >= size)
1132 return -1;
1133 if (strlcat(buf, ")", size) >= size)
1134 return -1;
1135 }
1136
1137 return 0;
1138 }
1139
1140 static int
schema_dump_attrlist(const char * desc,struct attr_list * alist,char * buf,size_t size)1141 schema_dump_attrlist(const char *desc, struct attr_list *alist,
1142 char *buf, size_t size)
1143 {
1144 struct attr_ptr *aptr;
1145
1146 if (alist == NULL || SLIST_EMPTY(alist))
1147 return 0;
1148
1149 if (strlcat(buf, " ", size) >= size ||
1150 strlcat(buf, desc, size) >= size)
1151 return -1;
1152
1153 aptr = SLIST_FIRST(alist);
1154 if (SLIST_NEXT(aptr, next) == NULL) {
1155 /* single attribute, no parenthesis */
1156 if (strlcat(buf, " ", size) >= size ||
1157 strlcat(buf, ATTR_NAME(aptr->attr_type), size) >= size)
1158 return -1;
1159 } else {
1160 if (strlcat(buf, " ( ", size) >= size)
1161 return -1;
1162 SLIST_FOREACH(aptr, alist, next) {
1163 if (strlcat(buf, ATTR_NAME(aptr->attr_type),
1164 size) >= size ||
1165 strlcat(buf, " ", size) >= size)
1166 return -1;
1167 if (SLIST_NEXT(aptr, next) != NULL &&
1168 strlcat(buf, "$ ", size) >= size)
1169 return -1;
1170 }
1171 if (strlcat(buf, ")", size) >= size)
1172 return -1;
1173 }
1174
1175 return 0;
1176 }
1177
1178 static int
schema_dump_objlist(const char * desc,struct obj_list * olist,char * buf,size_t size)1179 schema_dump_objlist(const char *desc, struct obj_list *olist,
1180 char *buf, size_t size)
1181 {
1182 struct obj_ptr *optr;
1183
1184 if (olist == NULL || SLIST_EMPTY(olist))
1185 return 0;
1186
1187 if (strlcat(buf, " ", size) >= size ||
1188 strlcat(buf, desc, size) >= size)
1189 return -1;
1190
1191 optr = SLIST_FIRST(olist);
1192 if (SLIST_NEXT(optr, next) == NULL) {
1193 /* single attribute, no parenthesis */
1194 if (strlcat(buf, " ", size) >= size ||
1195 strlcat(buf, OBJ_NAME(optr->object), size) >= size)
1196 return -1;
1197 } else {
1198 if (strlcat(buf, " ( ", size) >= size)
1199 return -1;
1200 SLIST_FOREACH(optr, olist, next) {
1201 if (strlcat(buf, OBJ_NAME(optr->object), size) >= size ||
1202 strlcat(buf, " ", size) >= size)
1203 return -1;
1204 if (SLIST_NEXT(optr, next) != NULL &&
1205 strlcat(buf, "$ ", size) >= size)
1206 return -1;
1207 }
1208 if (strlcat(buf, ")", size) >= size)
1209 return -1;
1210 }
1211
1212 return 0;
1213 }
1214
1215 int
schema_dump_object(struct object * obj,char * buf,size_t size)1216 schema_dump_object(struct object *obj, char *buf, size_t size)
1217 {
1218 if (strlcpy(buf, "( ", size) >= size ||
1219 strlcat(buf, obj->oid, size) >= size)
1220 return -1;
1221
1222 if (schema_dump_names("NAME", obj->names, buf, size) != 0)
1223 return -1;
1224
1225 if (obj->desc != NULL)
1226 if (strlcat(buf, " DESC '", size) >= size ||
1227 strlcat(buf, obj->desc, size) >= size ||
1228 strlcat(buf, "'", size) >= size)
1229 return -1;
1230
1231 switch (obj->kind) {
1232 case KIND_STRUCTURAL:
1233 if (strlcat(buf, " STRUCTURAL", size) >= size)
1234 return -1;
1235 break;
1236 case KIND_ABSTRACT:
1237 if (strlcat(buf, " ABSTRACT", size) >= size)
1238 return -1;
1239 break;
1240 case KIND_AUXILIARY:
1241 if (strlcat(buf, " AUXILIARY", size) >= size)
1242 return -1;
1243 break;
1244 }
1245
1246 if (schema_dump_objlist("SUP", obj->sup, buf, size) != 0)
1247 return -1;
1248
1249 if (obj->obsolete && strlcat(buf, " OBSOLETE", size) >= size)
1250 return -1;
1251
1252 if (schema_dump_attrlist("MUST", obj->must, buf, size) != 0)
1253 return -1;
1254
1255 if (schema_dump_attrlist("MAY", obj->may, buf, size) != 0)
1256 return -1;
1257
1258 if (strlcat(buf, " )", size) >= size)
1259 return -1;
1260
1261 return 0;
1262 }
1263
1264 int
schema_dump_attribute(struct attr_type * at,char * buf,size_t size)1265 schema_dump_attribute(struct attr_type *at, char *buf, size_t size)
1266 {
1267 if (strlcpy(buf, "( ", size) >= size ||
1268 strlcat(buf, at->oid, size) >= size)
1269 return -1;
1270
1271 if (schema_dump_names("NAME", at->names, buf, size) != 0)
1272 return -1;
1273
1274 if (at->desc != NULL)
1275 if (strlcat(buf, " DESC '", size) >= size ||
1276 strlcat(buf, at->desc, size) >= size ||
1277 strlcat(buf, "'", size) >= size)
1278 return -1;
1279
1280 if (at->obsolete && strlcat(buf, " OBSOLETE", size) >= size)
1281 return -1;
1282
1283 if (at->sup != NULL)
1284 if (strlcat(buf, " SUP ", size) >= size ||
1285 strlcat(buf, ATTR_NAME(at->sup), size) >= size)
1286 return -1;
1287
1288 if (at->equality != NULL)
1289 if (strlcat(buf, " EQUALITY ", size) >= size ||
1290 strlcat(buf, at->equality->name, size) >= size)
1291 return -1;
1292
1293 if (at->ordering != NULL)
1294 if (strlcat(buf, " ORDERING ", size) >= size ||
1295 strlcat(buf, at->ordering->name, size) >= size)
1296 return -1;
1297
1298 if (at->substr != NULL)
1299 if (strlcat(buf, " SUBSTR ", size) >= size ||
1300 strlcat(buf, at->substr->name, size) >= size)
1301 return -1;
1302
1303 if (at->syntax != NULL)
1304 if (strlcat(buf, " SYNTAX ", size) >= size ||
1305 strlcat(buf, at->syntax->oid, size) >= size)
1306 return -1;
1307
1308 if (at->single && strlcat(buf, " SINGLE-VALUE", size) >= size)
1309 return -1;
1310
1311 if (at->collective && strlcat(buf, " COLLECTIVE", size) >= size)
1312 return -1;
1313
1314 if (at->immutable && strlcat(buf, " NO-USER-MODIFICATION", size) >= size)
1315 return -1;
1316
1317 switch (at->usage) {
1318 case USAGE_USER_APP:
1319 /* User application usage is the default. */
1320 break;
1321 case USAGE_DIR_OP:
1322 if (strlcat(buf, " USAGE directoryOperation", size) >= size)
1323 return -1;
1324 break;
1325 case USAGE_DIST_OP:
1326 if (strlcat(buf, " USAGE distributedOperation", size) >= size)
1327 return -1;
1328 break;
1329 case USAGE_DSA_OP:
1330 if (strlcat(buf, " USAGE dSAOperation", size) >= size)
1331 return -1;
1332 break;
1333 }
1334
1335 if (strlcat(buf, " )", size) >= size)
1336 return -1;
1337
1338 return 0;
1339 }
1340
1341 int
schema_dump_match_rule(struct match_rule * mr,char * buf,size_t size)1342 schema_dump_match_rule(struct match_rule *mr, char *buf, size_t size)
1343 {
1344 if (strlcpy(buf, "( ", size) >= size ||
1345 strlcat(buf, mr->oid, size) >= size ||
1346 strlcat(buf, " NAME '", size) >= size ||
1347 strlcat(buf, mr->name, size) >= size ||
1348 strlcat(buf, "' SYNTAX ", size) >= size ||
1349 strlcat(buf, mr->syntax_oid, size) >= size ||
1350 strlcat(buf, " )", size) >= size)
1351 return -1;
1352
1353 return 0;
1354 }
1355
1356