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