xref: /openbsd/usr.sbin/ldapd/schema.c (revision 73471bf0)
1 /*	$OpenBSD: schema.c,v 1.19 2019/10/24 12:39:26 tb 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
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
39 obj_oid_cmp(struct object *a, struct object *b)
40 {
41 	return strcasecmp(a->oid, b->oid);
42 }
43 
44 static int
45 oidname_cmp(struct oidname *a, struct oidname *b)
46 {
47 	return strcasecmp(a->on_name, b->on_name);
48 }
49 
50 static int
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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
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 *
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
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
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
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
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 *
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
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
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
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
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
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 *
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
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 *
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 *
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
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
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 	}
838 
839 	/* Check that a syntax is defined, either directly or
840 	 * indirectly via a superior attribute type.
841 	 */
842 	sup = attr->sup;
843 	while (attr->syntax == NULL && sup != NULL) {
844 		attr->syntax = sup->syntax;
845 		sup = sup->sup;
846 	}
847 	if (attr->syntax == NULL) {
848 		schema_err(schema, "%s: no syntax defined", ATTR_NAME(attr));
849 		goto fail;
850 	}
851 
852 	/* If the attribute type doesn't explicitly define equality, check
853 	 * if any superior attribute type does.
854 	 */
855 	sup = attr->sup;
856 	while (attr->equality == NULL && sup != NULL) {
857 		attr->equality = sup->equality;
858 		sup = sup->sup;
859 	}
860 	/* Same thing with ordering matching rule. */
861 	sup = attr->sup;
862 	while (attr->ordering == NULL && sup != NULL) {
863 		attr->ordering = sup->ordering;
864 		sup = sup->sup;
865 	}
866 	/* ...and substring matching rule. */
867 	sup = attr->sup;
868 	while (attr->substr == NULL && sup != NULL) {
869 		attr->substr = sup->substr;
870 		sup = sup->sup;
871 	}
872 
873 	if (schema_validate_match_rule(schema, attr, attr->equality, MATCH_EQUALITY) != 0 ||
874 	    schema_validate_match_rule(schema, attr, attr->ordering, MATCH_ORDERING) != 0 ||
875 	    schema_validate_match_rule(schema, attr, attr->substr, MATCH_SUBSTR) != 0)
876 		goto fail;
877 
878 	return 0;
879 
880 fail:
881 	free(kw);
882 	if (attr != NULL) {
883 		if (attr->oid != NULL) {
884 			RB_REMOVE(attr_type_tree, &schema->attr_types, attr);
885 			free(attr->oid);
886 		}
887 		free(attr->desc);
888 		free(attr);
889 	}
890 	return -1;
891 }
892 
893 static int
894 schema_parse_objectclass(struct schema *schema)
895 {
896 	struct object		*obj = NULL, *prev;
897 	struct obj_ptr		*optr;
898 	struct name_list	*xnames;
899 	char			*kw = NULL;
900 	int			 token, ret = 0;
901 
902 	if (schema_lex(schema, NULL) != '(')
903 		goto fail;
904 
905 	if (schema_lex(schema, &kw) != STRING)
906 		goto fail;
907 
908 	if ((obj = calloc(1, sizeof(*obj))) == NULL) {
909 		log_warn("calloc");
910 		goto fail;
911 	}
912 	obj->kind = KIND_STRUCTURAL;
913 
914 	if (is_oidstr(kw))
915 		obj->oid = kw;
916 	else {
917 		obj->oid = lookup_symbolic_oid(schema, kw);
918 		if (obj->oid == NULL)
919 			goto fail;
920 		free(kw);
921 	}
922 	kw = NULL;
923 
924 	prev = RB_INSERT(object_tree, &schema->objects, obj);
925 	if (prev != NULL) {
926 		schema_err(schema, "object class %s already defined", obj->oid);
927 		goto fail;
928 	}
929 
930 	while (ret == 0) {
931 		token = schema_lex(schema, &kw);
932 		if (token == ')')
933 			break;
934 		else if (token != STRING)
935 			goto fail;
936 		if (strcasecmp(kw, "NAME") == 0) {
937 			obj->names = schema_parse_names(schema);
938 			if (obj->names == NULL)
939 				goto fail;
940 			schema_link_obj_names(schema, obj);
941 		} else if (strcasecmp(kw, "DESC") == 0) {
942 			if (schema_lex(schema, &obj->desc) != STRING)
943 				goto fail;
944 		} else if (strcasecmp(kw, "OBSOLETE") == 0) {
945 			obj->obsolete = 1;
946 		} else if (strcasecmp(kw, "SUP") == 0) {
947 			obj->sup = schema_parse_objlist(schema);
948 			if (obj->sup == NULL)
949 				goto fail;
950 		} else if (strcasecmp(kw, "ABSTRACT") == 0) {
951 			obj->kind = KIND_ABSTRACT;
952 		} else if (strcasecmp(kw, "STRUCTURAL") == 0) {
953 			obj->kind = KIND_STRUCTURAL;
954 		} else if (strcasecmp(kw, "AUXILIARY") == 0) {
955 			obj->kind = KIND_AUXILIARY;
956 		} else if (strcasecmp(kw, "MUST") == 0) {
957 			obj->must = schema_parse_attrlist(schema);
958 			if (obj->must == NULL)
959 				goto fail;
960 		} else if (strcasecmp(kw, "MAY") == 0) {
961 			obj->may = schema_parse_attrlist(schema);
962 			if (obj->may == NULL)
963 				goto fail;
964 		} else if (strncasecmp(kw, "X-", 2) == 0) {
965 			/* unknown extension, eat argument(s) */
966 			xnames = schema_parse_names(schema);
967 			if (xnames == NULL)
968 				goto fail;
969 			schema_free_name_list(xnames);
970 		} else {
971 			schema_err(schema, "syntax error at token '%s'", kw);
972 			goto fail;
973 		}
974 		free(kw);
975 	}
976 
977 	/* Verify the subclassing is allowed.
978 	 *
979 	 * Structural object classes cannot subclass auxiliary object classes.
980 	 * Auxiliary object classes cannot subclass structural object classes.
981 	 * Abstract object classes cannot derive from structural or auxiliary
982 	 *   object classes.
983 	 */
984 	if (obj->sup != NULL) {
985 		SLIST_FOREACH(optr, obj->sup, next) {
986 			if (obj->kind == KIND_STRUCTURAL &&
987 			    optr->object->kind == KIND_AUXILIARY) {
988 				log_warnx("structural object class '%s' cannot"
989 				    " subclass auxiliary object class '%s'",
990 				    OBJ_NAME(obj), OBJ_NAME(optr->object));
991 				goto fail;
992 			}
993 
994 			if (obj->kind == KIND_AUXILIARY &&
995 			    optr->object->kind == KIND_STRUCTURAL) {
996 				log_warnx("auxiliary object class '%s' cannot"
997 				    " subclass structural object class '%s'",
998 				    OBJ_NAME(obj), OBJ_NAME(optr->object));
999 				goto fail;
1000 			}
1001 
1002 			if (obj->kind == KIND_ABSTRACT &&
1003 			    optr->object->kind != KIND_ABSTRACT) {
1004 				log_warnx("abstract object class '%s' cannot"
1005 				    " subclass non-abstract object class '%s'",
1006 				    OBJ_NAME(obj), OBJ_NAME(optr->object));
1007 				goto fail;
1008 			}
1009 		}
1010 	}
1011 
1012 	return 0;
1013 
1014 fail:
1015 	free(kw);
1016 	if (obj != NULL) {
1017 		if (obj->oid != NULL) {
1018 			RB_REMOVE(object_tree, &schema->objects, obj);
1019 			free(obj->oid);
1020 		}
1021 		free(obj->desc);
1022 		free(obj);
1023 	}
1024 	return -1;
1025 }
1026 
1027 static int
1028 schema_parse_objectidentifier(struct schema *schema)
1029 {
1030 	char		*symname = NULL, *symoid = NULL;
1031 	char		*oid = NULL;
1032 
1033 	if (schema_lex(schema, &symname) != STRING)
1034 		goto fail;
1035 	if (schema_lex(schema, &symoid) != STRING)
1036 		goto fail;
1037 
1038 	if (is_oidstr(symoid)) {
1039 		oid = symoid;
1040 		symoid = NULL;
1041 	} else if ((oid = lookup_symbolic_oid(schema, symoid)) == NULL)
1042 		goto fail;
1043 
1044 	if (push_symbolic_oid(schema, symname, oid) == NULL)
1045 		goto fail;
1046 
1047 	free(symoid);
1048 	return 0;
1049 
1050 fail:
1051 	free(symname);
1052 	free(symoid);
1053 	free(oid);
1054 	return -1;
1055 }
1056 
1057 int
1058 schema_parse(struct schema *schema, const char *filename)
1059 {
1060 	char	*kw;
1061 	int	 token, ret = 0;
1062 
1063 	log_debug("parsing schema file '%s'", filename);
1064 
1065 	if ((schema->fp = fopen(filename, "r")) == NULL) {
1066 		log_warn("%s", filename);
1067 		return -1;
1068 	}
1069 	schema->filename = filename;
1070 	schema->lineno = 1;
1071 
1072 	while (ret == 0) {
1073 		token = schema_lex(schema, &kw);
1074 		if (token == STRING) {
1075 			if (strcasecmp(kw, "attributetype") == 0)
1076 				ret = schema_parse_attributetype(schema);
1077 			else if (strcasecmp(kw, "objectclass") == 0)
1078 				ret = schema_parse_objectclass(schema);
1079 			else if (strcasecmp(kw, "objectidentifier") == 0)
1080 				ret = schema_parse_objectidentifier(schema);
1081 			else {
1082 				schema_err(schema, "syntax error at '%s'", kw);
1083 				ret = -1;
1084 			}
1085 			if (ret == -1 && schema->error == 0)
1086 				schema_err(schema, "syntax error");
1087 			free(kw);
1088 		} else if (token == 0) {	/* EOF */
1089 			break;
1090 		} else {
1091 			schema_err(schema, "syntax error");
1092 			ret = -1;
1093 		}
1094 	}
1095 
1096 	fclose(schema->fp);
1097 	schema->fp = NULL;
1098 	schema->filename = NULL;
1099 
1100 	return ret;
1101 }
1102 
1103 static int
1104 schema_dump_names(const char *desc, struct name_list *nlist,
1105     char *buf, size_t size)
1106 {
1107 	struct name	*name;
1108 
1109 	if (nlist == NULL || SLIST_EMPTY(nlist))
1110 		return 0;
1111 
1112 	if (strlcat(buf, " ", size) >= size ||
1113 	    strlcat(buf, desc, size) >= size)
1114 		return -1;
1115 
1116 	name = SLIST_FIRST(nlist);
1117 	if (SLIST_NEXT(name, next) == NULL) {
1118 		/* single name, no parenthesis */
1119 		if (strlcat(buf, " '", size) >= size ||
1120 		    strlcat(buf, name->name, size) >= size ||
1121 		    strlcat(buf, "'", size) >= size)
1122 			return -1;
1123 	} else {
1124 		if (strlcat(buf, " ( ", size) >= size)
1125 			return -1;
1126 		SLIST_FOREACH(name, nlist, next)
1127 			if (strlcat(buf, "'", size) >= size ||
1128 			    strlcat(buf, name->name, size) >= size ||
1129 			    strlcat(buf, "' ", size) >= size)
1130 				return -1;
1131 		if (strlcat(buf, ")", size) >= size)
1132 			return -1;
1133 	}
1134 
1135 	return 0;
1136 }
1137 
1138 static int
1139 schema_dump_attrlist(const char *desc, struct attr_list *alist,
1140     char *buf, size_t size)
1141 {
1142 	struct attr_ptr		*aptr;
1143 
1144 	if (alist == NULL || SLIST_EMPTY(alist))
1145 		return 0;
1146 
1147 	if (strlcat(buf, " ", size) >= size ||
1148 	    strlcat(buf, desc, size) >= size)
1149 		return -1;
1150 
1151 	aptr = SLIST_FIRST(alist);
1152 	if (SLIST_NEXT(aptr, next) == NULL) {
1153 		/* single attribute, no parenthesis */
1154 		if (strlcat(buf, " ", size) >= size ||
1155 		    strlcat(buf, ATTR_NAME(aptr->attr_type), size) >= size)
1156 			return -1;
1157 	} else {
1158 		if (strlcat(buf, " ( ", size) >= size)
1159 			return -1;
1160 		SLIST_FOREACH(aptr, alist, next) {
1161 			if (strlcat(buf, ATTR_NAME(aptr->attr_type),
1162 			    size) >= size ||
1163 			    strlcat(buf, " ", size) >= size)
1164 				return -1;
1165 			if (SLIST_NEXT(aptr, next) != NULL &&
1166 			    strlcat(buf, "$ ", size) >= size)
1167 				return -1;
1168 		}
1169 		if (strlcat(buf, ")", size) >= size)
1170 			return -1;
1171 	}
1172 
1173 	return 0;
1174 }
1175 
1176 static int
1177 schema_dump_objlist(const char *desc, struct obj_list *olist,
1178     char *buf, size_t size)
1179 {
1180 	struct obj_ptr		*optr;
1181 
1182 	if (olist == NULL || SLIST_EMPTY(olist))
1183 		return 0;
1184 
1185 	if (strlcat(buf, " ", size) >= size ||
1186 	    strlcat(buf, desc, size) >= size)
1187 		return -1;
1188 
1189 	optr = SLIST_FIRST(olist);
1190 	if (SLIST_NEXT(optr, next) == NULL) {
1191 		/* single attribute, no parenthesis */
1192 		if (strlcat(buf, " ", size) >= size ||
1193 		    strlcat(buf, OBJ_NAME(optr->object), size) >= size)
1194 			return -1;
1195 	} else {
1196 		if (strlcat(buf, " ( ", size) >= size)
1197 			return -1;
1198 		SLIST_FOREACH(optr, olist, next) {
1199 			if (strlcat(buf, OBJ_NAME(optr->object), size) >= size ||
1200 			    strlcat(buf, " ", size) >= size)
1201 				return -1;
1202 			if (SLIST_NEXT(optr, next) != NULL &&
1203 			    strlcat(buf, "$ ", size) >= size)
1204 				return -1;
1205 		}
1206 		if (strlcat(buf, ")", size) >= size)
1207 			return -1;
1208 	}
1209 
1210 	return 0;
1211 }
1212 
1213 int
1214 schema_dump_object(struct object *obj, char *buf, size_t size)
1215 {
1216 	if (strlcpy(buf, "( ", size) >= size ||
1217 	    strlcat(buf, obj->oid, size) >= size)
1218 		return -1;
1219 
1220 	if (schema_dump_names("NAME", obj->names, buf, size) != 0)
1221 		return -1;
1222 
1223 	if (obj->desc != NULL)
1224 		if (strlcat(buf, " DESC '", size) >= size ||
1225 		    strlcat(buf, obj->desc, size) >= size ||
1226 		    strlcat(buf, "'", size) >= size)
1227 			return -1;
1228 
1229 	switch (obj->kind) {
1230 	case KIND_STRUCTURAL:
1231 		if (strlcat(buf, " STRUCTURAL", size) >= size)
1232 			return -1;
1233 		break;
1234 	case KIND_ABSTRACT:
1235 		if (strlcat(buf, " ABSTRACT", size) >= size)
1236 			return -1;
1237 		break;
1238 	case KIND_AUXILIARY:
1239 		if (strlcat(buf, " AUXILIARY", size) >= size)
1240 			return -1;
1241 		break;
1242 	}
1243 
1244 	if (schema_dump_objlist("SUP", obj->sup, buf, size) != 0)
1245 		return -1;
1246 
1247 	if (obj->obsolete && strlcat(buf, " OBSOLETE", size) >= size)
1248 		return -1;
1249 
1250 	if (schema_dump_attrlist("MUST", obj->must, buf, size) != 0)
1251 		return -1;
1252 
1253 	if (schema_dump_attrlist("MAY", obj->may, buf, size) != 0)
1254 		return -1;
1255 
1256 	if (strlcat(buf, " )", size) >= size)
1257 		return -1;
1258 
1259 	return 0;
1260 }
1261 
1262 int
1263 schema_dump_attribute(struct attr_type *at, char *buf, size_t size)
1264 {
1265 	if (strlcpy(buf, "( ", size) >= size ||
1266 	    strlcat(buf, at->oid, size) >= size)
1267 		return -1;
1268 
1269 	if (schema_dump_names("NAME", at->names, buf, size) != 0)
1270 		return -1;
1271 
1272 	if (at->desc != NULL)
1273 		if (strlcat(buf, " DESC '", size) >= size ||
1274 		    strlcat(buf, at->desc, size) >= size ||
1275 		    strlcat(buf, "'", size) >= size)
1276 			return -1;
1277 
1278 	if (at->obsolete && strlcat(buf, " OBSOLETE", size) >= size)
1279 		return -1;
1280 
1281 	if (at->sup != NULL)
1282 		if (strlcat(buf, " SUP ", size) >= size ||
1283 		    strlcat(buf, ATTR_NAME(at->sup), size) >= size)
1284 			return -1;
1285 
1286 	if (at->equality != NULL)
1287 		if (strlcat(buf, " EQUALITY ", size) >= size ||
1288 		    strlcat(buf, at->equality->name, size) >= size)
1289 			return -1;
1290 
1291 	if (at->ordering != NULL)
1292 		if (strlcat(buf, " ORDERING ", size) >= size ||
1293 		    strlcat(buf, at->ordering->name, size) >= size)
1294 			return -1;
1295 
1296 	if (at->substr != NULL)
1297 		if (strlcat(buf, " SUBSTR ", size) >= size ||
1298 		    strlcat(buf, at->substr->name, size) >= size)
1299 			return -1;
1300 
1301 	if (at->syntax != NULL)
1302 		if (strlcat(buf, " SYNTAX ", size) >= size ||
1303 		    strlcat(buf, at->syntax->oid, size) >= size)
1304 			return -1;
1305 
1306 	if (at->single && strlcat(buf, " SINGLE-VALUE", size) >= size)
1307 		return -1;
1308 
1309 	if (at->collective && strlcat(buf, " COLLECTIVE", size) >= size)
1310 		return -1;
1311 
1312 	if (at->immutable && strlcat(buf, " NO-USER-MODIFICATION", size) >= size)
1313 		return -1;
1314 
1315 	switch (at->usage) {
1316 	case USAGE_USER_APP:
1317 		/* User application usage is the default. */
1318 		break;
1319 	case USAGE_DIR_OP:
1320 		if (strlcat(buf, " USAGE directoryOperation", size) >= size)
1321 			return -1;
1322 		break;
1323 	case USAGE_DIST_OP:
1324 		if (strlcat(buf, " USAGE distributedOperation", size) >= size)
1325 			return -1;
1326 		break;
1327 	case USAGE_DSA_OP:
1328 		if (strlcat(buf, " USAGE dSAOperation", size) >= size)
1329 			return -1;
1330 		break;
1331 	}
1332 
1333 	if (strlcat(buf, " )", size) >= size)
1334 		return -1;
1335 
1336 	return 0;
1337 }
1338 
1339 int
1340 schema_dump_match_rule(struct match_rule *mr, char *buf, size_t size)
1341 {
1342 	if (strlcpy(buf, "( ", size) >= size ||
1343 	    strlcat(buf, mr->oid, size) >= size ||
1344 	    strlcat(buf, " NAME '", size) >= size ||
1345 	    strlcat(buf, mr->name, size) >= size ||
1346 	    strlcat(buf, "' SYNTAX ", size) >= size ||
1347 	    strlcat(buf, mr->syntax_oid, size) >= size ||
1348 	    strlcat(buf, " )", size) >= size)
1349 		return -1;
1350 
1351 	return 0;
1352 }
1353 
1354