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