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