1 /*	$NetBSD: ad.c,v 1.3 2021/08/14 16:14:58 christos Exp $	*/
2 
3 /* ad.c - routines for dealing with attribute descriptions */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1998-2021 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 
19 #include <sys/cdefs.h>
20 __RCSID("$NetBSD: ad.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
21 
22 #include "portable.h"
23 
24 #include <stdio.h>
25 
26 #include <ac/ctype.h>
27 #include <ac/errno.h>
28 #include <ac/socket.h>
29 #include <ac/string.h>
30 #include <ac/time.h>
31 
32 #include "slap.h"
33 #include "lutil.h"
34 
35 static struct berval bv_no_attrs = BER_BVC( LDAP_NO_ATTRS );
36 static struct berval bv_all_user_attrs = BER_BVC( "*" );
37 static struct berval bv_all_operational_attrs = BER_BVC( "+" );
38 
39 static AttributeName anlist_no_attrs[] = {
40 	{ BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL },
41 	{ BER_BVNULL, NULL, 0, NULL }
42 };
43 
44 static AttributeName anlist_all_user_attributes[] = {
45 	{ BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL },
46 	{ BER_BVNULL, NULL, 0, NULL }
47 };
48 
49 static AttributeName anlist_all_operational_attributes[] = {
50 	{ BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL },
51 	{ BER_BVNULL, NULL, 0, NULL }
52 };
53 
54 static AttributeName anlist_all_attributes[] = {
55 	{ BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL },
56 	{ BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL },
57 	{ BER_BVNULL, NULL, 0, NULL }
58 };
59 
60 AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
61 AttributeName *slap_anlist_all_user_attributes = anlist_all_user_attributes;
62 AttributeName *slap_anlist_all_operational_attributes = anlist_all_operational_attributes;
63 AttributeName *slap_anlist_all_attributes = anlist_all_attributes;
64 
65 struct berval * slap_bv_no_attrs = &bv_no_attrs;
66 struct berval * slap_bv_all_user_attrs = &bv_all_user_attrs;
67 struct berval * slap_bv_all_operational_attrs = &bv_all_operational_attrs;
68 
69 typedef struct Attr_option {
70 	struct berval name;	/* option name or prefix */
71 	int           prefix;	/* NAME is a tag and range prefix */
72 } Attr_option;
73 
74 static Attr_option lang_option = { BER_BVC("lang-"), 1 };
75 
76 /* Options sorted by name, and number of options */
77 static Attr_option *options = &lang_option;
78 static int option_count = 1;
79 
80 static int msad_range_hack = 0;
81 
82 static int ad_count;
83 
84 static Attr_option *ad_find_option_definition( const char *opt, int optlen );
85 
ad_keystring(struct berval * bv)86 int ad_keystring(
87 	struct berval *bv )
88 {
89 	ber_len_t i;
90 
91 	if( !AD_LEADCHAR( bv->bv_val[0] ) ) {
92 		return 1;
93 	}
94 
95 	for( i=1; i<bv->bv_len; i++ ) {
96 		if( !AD_CHAR( bv->bv_val[i] )) {
97 			if ( msad_range_hack && bv->bv_val[i] == '=' )
98 				continue;
99 			return 1;
100 		}
101 	}
102 	return 0;
103 }
104 
ad_destroy(AttributeDescription * ad)105 void ad_destroy( AttributeDescription *ad )
106 {
107 	AttributeDescription *n;
108 
109 	for (; ad != NULL; ad = n) {
110 		n = ad->ad_next;
111 		ldap_memfree( ad );
112 	}
113 }
114 
115 /* Is there an AttributeDescription for this type that uses these tags? */
ad_find_tags(AttributeType * type,struct berval * tags)116 AttributeDescription * ad_find_tags(
117 	AttributeType *type,
118 	struct berval *tags )
119 {
120 	AttributeDescription *ad;
121 
122 	ldap_pvt_thread_mutex_lock( &type->sat_ad_mutex );
123 	for (ad = type->sat_ad; ad; ad=ad->ad_next)
124 	{
125 		if (ad->ad_tags.bv_len == tags->bv_len &&
126 			!strcasecmp(ad->ad_tags.bv_val, tags->bv_val))
127 			break;
128 	}
129 	ldap_pvt_thread_mutex_unlock( &type->sat_ad_mutex );
130 	return ad;
131 }
132 
slap_str2ad(const char * str,AttributeDescription ** ad,const char ** text)133 int slap_str2ad(
134 	const char *str,
135 	AttributeDescription **ad,
136 	const char **text )
137 {
138 	struct berval bv;
139 	bv.bv_val = (char *) str;
140 	bv.bv_len = strlen( str );
141 
142 	return slap_bv2ad( &bv, ad, text );
143 }
144 
strchrlen(const char * beg,const char * end,const char ch,int * len)145 static char *strchrlen(
146 	const char *beg,
147 	const char *end,
148 	const char ch,
149 	int *len )
150 {
151 	const char *p;
152 
153 	for( p=beg; p < end && *p; p++ ) {
154 		if( *p == ch ) {
155 			*len = p - beg;
156 			return (char *) p;
157 		}
158 	}
159 
160 	*len = p - beg;
161 	return NULL;
162 }
163 
slap_bv2ad(struct berval * bv,AttributeDescription ** ad,const char ** text)164 int slap_bv2ad(
165 	struct berval *bv,
166 	AttributeDescription **ad,
167 	const char **text )
168 {
169 	int rtn = LDAP_UNDEFINED_TYPE;
170 	AttributeDescription desc, *d2;
171 	char *name, *options, *optn;
172 	char *opt, *next;
173 	int ntags;
174 	int tagslen;
175 
176 	/* hardcoded limits for speed */
177 #define MAX_TAGGING_OPTIONS 128
178 	struct berval tags[MAX_TAGGING_OPTIONS+1];
179 #define MAX_TAGS_LEN 1024
180 	char tagbuf[MAX_TAGS_LEN];
181 
182 	assert( ad != NULL );
183 	assert( *ad == NULL ); /* temporary */
184 
185 	if( bv == NULL || BER_BVISNULL( bv ) || BER_BVISEMPTY( bv ) ) {
186 		*text = "empty AttributeDescription";
187 		return rtn;
188 	}
189 
190 	/* make sure description is IA5 */
191 	if( ad_keystring( bv ) ) {
192 		*text = "AttributeDescription contains inappropriate characters";
193 		return rtn;
194 	}
195 
196 	/* find valid base attribute type; parse in place */
197 	desc.ad_cname = *bv;
198 	desc.ad_flags = 0;
199 	BER_BVZERO( &desc.ad_tags );
200 	name = bv->bv_val;
201 	options = ber_bvchr( bv, ';' );
202 	if ( options != NULL && (unsigned) ( options - name ) < bv->bv_len ) {
203 		/* don't go past the end of the berval! */
204 		desc.ad_cname.bv_len = options - name;
205 	} else {
206 		options = NULL;
207 	}
208 	desc.ad_type = at_bvfind( &desc.ad_cname );
209 	if( desc.ad_type == NULL ) {
210 		*text = "attribute type undefined";
211 		return rtn;
212 	}
213 
214 	if( is_at_operational( desc.ad_type ) && options != NULL ) {
215 		*text = "operational attribute with options undefined";
216 		return rtn;
217 	}
218 
219 	/*
220 	 * parse options in place
221 	 */
222 	ntags = 0;
223 	tagslen = 0;
224 	optn = bv->bv_val + bv->bv_len;
225 
226 	for( opt=options; opt != NULL; opt=next ) {
227 		Attr_option *aopt;
228 		int optlen;
229 		opt++;
230 		next = strchrlen( opt, optn, ';', &optlen );
231 
232 		if( optlen == 0 ) {
233 			*text = "zero length option is invalid";
234 			return rtn;
235 
236 		} else if ( optlen == STRLENOF("binary") &&
237 			strncasecmp( opt, "binary", STRLENOF("binary") ) == 0 )
238 		{
239 			/* binary option */
240 			if( slap_ad_is_binary( &desc ) ) {
241 				*text = "option \"binary\" specified multiple times";
242 				return rtn;
243 			}
244 
245 			if( !slap_syntax_is_binary( desc.ad_type->sat_syntax )) {
246 				/* not stored in binary, disallow option */
247 				*text = "option \"binary\" not supported with type";
248 				return rtn;
249 			}
250 
251 			desc.ad_flags |= SLAP_DESC_BINARY;
252 			continue;
253 
254 		} else if (( aopt = ad_find_option_definition( opt, optlen )) ) {
255 			int i;
256 
257 			if( opt[optlen-1] == '-' ||
258 				( aopt->name.bv_val[aopt->name.bv_len-1] == '=' && msad_range_hack )) {
259 				desc.ad_flags |= SLAP_DESC_TAG_RANGE;
260 			}
261 
262 			if( ntags >= MAX_TAGGING_OPTIONS ) {
263 				*text = "too many tagging options";
264 				return rtn;
265 			}
266 
267 			/*
268 			 * tags should be presented in sorted order,
269 			 * so run the array in reverse.
270 			 */
271 			for( i=ntags-1; i>=0; i-- ) {
272 				int rc;
273 
274 				rc = strncasecmp( opt, tags[i].bv_val,
275 					(unsigned) optlen < tags[i].bv_len
276 						? (unsigned) optlen : tags[i].bv_len );
277 
278 				if( rc == 0 && (unsigned)optlen == tags[i].bv_len ) {
279 					/* duplicate (ignore) */
280 					ntags--;
281 					goto done;
282 
283 				} else if ( rc > 0 ||
284 					( rc == 0 && (unsigned)optlen > tags[i].bv_len ))
285 				{
286 					AC_MEMCPY( &tags[i+2], &tags[i+1],
287 						(ntags-i-1)*sizeof(struct berval) );
288 					tags[i+1].bv_val = opt;
289 					tags[i+1].bv_len = optlen;
290 					goto done;
291 				}
292 			}
293 
294 			if( ntags ) {
295 				AC_MEMCPY( &tags[1], &tags[0],
296 					ntags*sizeof(struct berval) );
297 			}
298 			tags[0].bv_val = opt;
299 			tags[0].bv_len = optlen;
300 
301 done:;
302 			tagslen += optlen + 1;
303 			ntags++;
304 
305 		} else {
306 			*text = "unrecognized option";
307 			return rtn;
308 		}
309 	}
310 
311 	if( ntags > 0 ) {
312 		int i;
313 
314 		if( tagslen > MAX_TAGS_LEN ) {
315 			*text = "tagging options too long";
316 			return rtn;
317 		}
318 
319 		desc.ad_tags.bv_val = tagbuf;
320 		tagslen = 0;
321 
322 		for( i=0; i<ntags; i++ ) {
323 			AC_MEMCPY( &desc.ad_tags.bv_val[tagslen],
324 				tags[i].bv_val, tags[i].bv_len );
325 
326 			tagslen += tags[i].bv_len;
327 			desc.ad_tags.bv_val[tagslen++] = ';';
328 		}
329 
330 		desc.ad_tags.bv_val[--tagslen] = '\0';
331 		desc.ad_tags.bv_len = tagslen;
332 	}
333 
334 	/* see if a matching description is already cached */
335 	for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
336 		if( d2->ad_flags != desc.ad_flags ) {
337 			continue;
338 		}
339 		if( d2->ad_tags.bv_len != desc.ad_tags.bv_len ) {
340 			continue;
341 		}
342 		if( d2->ad_tags.bv_len == 0 ) {
343 			break;
344 		}
345 		if( strncasecmp( d2->ad_tags.bv_val, desc.ad_tags.bv_val,
346 			desc.ad_tags.bv_len ) == 0 )
347 		{
348 			break;
349 		}
350 	}
351 
352 	/* Not found, add new one */
353 	while (d2 == NULL) {
354 		size_t dlen = 0;
355 		ldap_pvt_thread_mutex_lock( &desc.ad_type->sat_ad_mutex );
356 		/* check again now that we've locked */
357 		for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
358 			if (d2->ad_flags != desc.ad_flags)
359 				continue;
360 			if (d2->ad_tags.bv_len != desc.ad_tags.bv_len)
361 				continue;
362 			if (d2->ad_tags.bv_len == 0)
363 				break;
364 			if (strncasecmp(d2->ad_tags.bv_val, desc.ad_tags.bv_val,
365 				desc.ad_tags.bv_len) == 0)
366 				break;
367 		}
368 		if (d2) {
369 			ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex );
370 			break;
371 		}
372 
373 		/* Allocate a single contiguous block. If there are no
374 		 * options, we just need space for the AttrDesc structure.
375 		 * Otherwise, we need to tack on the full name length +
376 		 * options length, + maybe tagging options length again.
377 		 */
378 		if (desc.ad_tags.bv_len || desc.ad_flags != SLAP_DESC_NONE) {
379 			dlen = desc.ad_type->sat_cname.bv_len + 1;
380 			if (desc.ad_tags.bv_len) {
381 				dlen += 1 + desc.ad_tags.bv_len;
382 			}
383 			if ( slap_ad_is_binary( &desc ) ) {
384 				dlen += 1 + STRLENOF(";binary") + desc.ad_tags.bv_len;
385 			}
386 		}
387 
388 		d2 = ch_malloc(sizeof(AttributeDescription) + dlen);
389 		d2->ad_next = NULL;
390 		d2->ad_type = desc.ad_type;
391 		d2->ad_flags = desc.ad_flags;
392 		d2->ad_cname.bv_len = desc.ad_type->sat_cname.bv_len;
393 		d2->ad_tags.bv_len = desc.ad_tags.bv_len;
394 		ldap_pvt_thread_mutex_lock( &ad_index_mutex );
395 		d2->ad_index = ++ad_count;
396 		ldap_pvt_thread_mutex_unlock( &ad_index_mutex );
397 
398 		if (dlen == 0) {
399 			d2->ad_cname.bv_val = d2->ad_type->sat_cname.bv_val;
400 			d2->ad_tags.bv_val = NULL;
401 		} else {
402 			char *cp, *op, *lp;
403 			int j;
404 			d2->ad_cname.bv_val = (char *)(d2+1);
405 			strcpy(d2->ad_cname.bv_val, d2->ad_type->sat_cname.bv_val);
406 			cp = d2->ad_cname.bv_val + d2->ad_cname.bv_len;
407 			if( slap_ad_is_binary( &desc ) ) {
408 				op = cp;
409 				lp = NULL;
410 				if( desc.ad_tags.bv_len ) {
411 					lp = desc.ad_tags.bv_val;
412 					while( strncasecmp(lp, "binary", STRLENOF("binary")) < 0
413 					       && (lp = strchr( lp, ';' )) != NULL )
414 						++lp;
415 					if( lp != desc.ad_tags.bv_val ) {
416 						*cp++ = ';';
417 						j = (lp
418 						     ? (unsigned) (lp - desc.ad_tags.bv_val - 1)
419 						     : strlen( desc.ad_tags.bv_val ));
420 						cp = lutil_strncopy(cp, desc.ad_tags.bv_val, j);
421 					}
422 				}
423 				cp = lutil_strcopy(cp, ";binary");
424 				if( lp != NULL ) {
425 					*cp++ = ';';
426 					cp = lutil_strcopy(cp, lp);
427 				}
428 				d2->ad_cname.bv_len = cp - d2->ad_cname.bv_val;
429 				if( desc.ad_tags.bv_len )
430 					ldap_pvt_str2lower(op);
431 				j = 1;
432 			} else {
433 				j = 0;
434 			}
435 			if( desc.ad_tags.bv_len ) {
436 				lp = d2->ad_cname.bv_val + d2->ad_cname.bv_len + j;
437 				if ( j == 0 )
438 					*lp++ = ';';
439 				d2->ad_tags.bv_val = lp;
440 				strcpy(lp, desc.ad_tags.bv_val);
441 				ldap_pvt_str2lower(lp);
442 				if( j == 0 )
443 					d2->ad_cname.bv_len += 1 + desc.ad_tags.bv_len;
444 			}
445 		}
446 		/* Add new desc to list. We always want the bare Desc with
447 		 * no options to stay at the head of the list, assuming
448 		 * that one will be used most frequently.
449 		 */
450 		if (desc.ad_type->sat_ad == NULL || dlen == 0) {
451 			d2->ad_next = desc.ad_type->sat_ad;
452 			desc.ad_type->sat_ad = d2;
453 		} else {
454 			d2->ad_next = desc.ad_type->sat_ad->ad_next;
455 			desc.ad_type->sat_ad->ad_next = d2;
456 		}
457 		ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex );
458 	}
459 
460 	if( *ad == NULL ) {
461 		*ad = d2;
462 	} else {
463 		**ad = *d2;
464 	}
465 
466 	return LDAP_SUCCESS;
467 }
468 
is_ad_subtags(struct berval * subtagsbv,struct berval * suptagsbv)469 static int is_ad_subtags(
470 	struct berval *subtagsbv,
471 	struct berval *suptagsbv )
472 {
473 	const char *suptags, *supp, *supdelimp, *supn;
474 	const char *subtags, *subp, *subdelimp, *subn;
475 	int  suplen, sublen;
476 
477 	subtags =subtagsbv->bv_val;
478 	suptags =suptagsbv->bv_val;
479 	subn = subtags + subtagsbv->bv_len;
480 	supn = suptags + suptagsbv->bv_len;
481 
482 	for( supp=suptags ; supp; supp=supdelimp ) {
483 		supdelimp = strchrlen( supp, supn, ';', &suplen );
484 		if( supdelimp ) supdelimp++;
485 
486 		for( subp=subtags ; subp; subp=subdelimp ) {
487 			subdelimp = strchrlen( subp, subn, ';', &sublen );
488 			if( subdelimp ) subdelimp++;
489 
490 			if ( suplen > sublen
491 				 ? ( suplen-1 == sublen && supp[suplen-1] == '-'
492 					 && strncmp( supp, subp, sublen ) == 0 )
493 				 : ( ( suplen == sublen || supp[suplen-1] == '-' )
494 					 && strncmp( supp, subp, suplen ) == 0 ) )
495 			{
496 				goto match;
497 			}
498 		}
499 
500 		return 0;
501 match:;
502 	}
503 	return 1;
504 }
505 
is_ad_subtype(AttributeDescription * sub,AttributeDescription * super)506 int is_ad_subtype(
507 	AttributeDescription *sub,
508 	AttributeDescription *super
509 )
510 {
511 	AttributeType *a;
512 	int lr;
513 
514 	for ( a = sub->ad_type; a; a=a->sat_sup ) {
515 		if ( a == super->ad_type ) break;
516 	}
517 	if( !a ) {
518 		return 0;
519 	}
520 
521 	/* ensure sub does support all flags of super */
522 	lr = sub->ad_tags.bv_len ? SLAP_DESC_TAG_RANGE : 0;
523 	if(( super->ad_flags & ( sub->ad_flags | lr )) != super->ad_flags ) {
524 		return 0;
525 	}
526 
527 	/* check for tagging options */
528 	if ( super->ad_tags.bv_len == 0 )
529 		return 1;
530 	if ( sub->ad_tags.bv_len == 0 )
531 		return 0;
532 
533 	return is_ad_subtags( &sub->ad_tags, &super->ad_tags );
534 }
535 
ad_inlist(AttributeDescription * desc,AttributeName * attrs)536 int ad_inlist(
537 	AttributeDescription *desc,
538 	AttributeName *attrs )
539 {
540 	if (! attrs ) return 0;
541 
542 	for( ; attrs->an_name.bv_val; attrs++ ) {
543 		AttributeType *a;
544 		ObjectClass *oc;
545 
546 		if ( attrs->an_desc ) {
547 			int lr;
548 
549 			if ( desc == attrs->an_desc ) {
550 				return 1;
551 			}
552 
553 			/*
554 			 * EXTENSION: if requested description is preceded by
555 			 * a '-' character, do not match on subtypes.
556 			 */
557 			if ( attrs->an_name.bv_val[0] == '-' ) {
558 				continue;
559 			}
560 
561 			/* Is this a subtype of the requested attr? */
562 			for (a = desc->ad_type; a; a=a->sat_sup) {
563 				if ( a == attrs->an_desc->ad_type )
564 					break;
565 			}
566 			if ( !a ) {
567 				continue;
568 			}
569 			/* Does desc support all the requested flags? */
570 			lr = desc->ad_tags.bv_len ? SLAP_DESC_TAG_RANGE : 0;
571 			if(( attrs->an_desc->ad_flags & (desc->ad_flags | lr))
572 				!= attrs->an_desc->ad_flags ) {
573 				continue;
574 			}
575 			/* Do the descs have compatible tags? */
576 			if ( attrs->an_desc->ad_tags.bv_len == 0 ) {
577 				return 1;
578 			}
579 			if ( desc->ad_tags.bv_len == 0) {
580 				continue;
581 			}
582 			if ( is_ad_subtags( &desc->ad_tags,
583 				&attrs->an_desc->ad_tags ) ) {
584 				return 1;
585 			}
586 			continue;
587 		}
588 
589 		if ( ber_bvccmp( &attrs->an_name, '*' ) ) {
590 			if ( !is_at_operational( desc->ad_type ) ) {
591 				return 1;
592 			}
593 			continue;
594 		}
595 
596 		if ( ber_bvccmp( &attrs->an_name, '+' ) ) {
597 			if ( is_at_operational( desc->ad_type ) ) {
598 				return 1;
599 			}
600 			continue;
601 		}
602 
603 		/*
604 		 * EXTENSION: see if requested description is @objectClass
605 		 * if so, return attributes which the class requires/allows
606 		 * else if requested description is !objectClass, return
607 		 * attributes which the class does not require/allow
608 		 */
609 		if ( !( attrs->an_flags & SLAP_AN_OCINITED )) {
610 			if( attrs->an_name.bv_val ) {
611 				switch( attrs->an_name.bv_val[0] ) {
612 				case '@': /* @objectClass */
613 				case '+': /* +objectClass (deprecated) */
614 				case '!': { /* exclude */
615 						struct berval ocname;
616 						ocname.bv_len = attrs->an_name.bv_len - 1;
617 						ocname.bv_val = &attrs->an_name.bv_val[1];
618 						oc = oc_bvfind( &ocname );
619 						if ( oc && attrs->an_name.bv_val[0] == '!' ) {
620 							attrs->an_flags |= SLAP_AN_OCEXCLUDE;
621 						} else {
622 							attrs->an_flags &= ~SLAP_AN_OCEXCLUDE;
623 						}
624 					} break;
625 
626 				default: /* old (deprecated) way */
627 					oc = oc_bvfind( &attrs->an_name );
628 				}
629 				attrs->an_oc = oc;
630 			}
631 			attrs->an_flags |= SLAP_AN_OCINITED;
632 		}
633 		oc = attrs->an_oc;
634 		if( oc != NULL ) {
635 			if ( attrs->an_flags & SLAP_AN_OCEXCLUDE ) {
636 				if ( oc == slap_schema.si_oc_extensibleObject ) {
637 					/* extensibleObject allows the return of anything */
638 					return 0;
639 				}
640 
641 				if( oc->soc_required ) {
642 					/* allow return of required attributes */
643 					int i;
644 
645    					for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
646 						for (a = desc->ad_type; a; a=a->sat_sup) {
647 							if ( a == oc->soc_required[i] ) {
648 								return 0;
649 							}
650 						}
651 					}
652 				}
653 
654 				if( oc->soc_allowed ) {
655 					/* allow return of allowed attributes */
656 					int i;
657    					for ( i = 0; oc->soc_allowed[i] != NULL; i++ ) {
658 						for (a = desc->ad_type; a; a=a->sat_sup) {
659 							if ( a == oc->soc_allowed[i] ) {
660 								return 0;
661 							}
662 						}
663 					}
664 				}
665 
666 				return 1;
667 			}
668 
669 			if ( oc == slap_schema.si_oc_extensibleObject ) {
670 				/* extensibleObject allows the return of anything */
671 				return 1;
672 			}
673 
674 			if( oc->soc_required ) {
675 				/* allow return of required attributes */
676 				int i;
677 
678    				for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
679 					for (a = desc->ad_type; a; a=a->sat_sup) {
680 						if ( a == oc->soc_required[i] ) {
681 							return 1;
682 						}
683 					}
684 				}
685 			}
686 
687 			if( oc->soc_allowed ) {
688 				/* allow return of allowed attributes */
689 				int i;
690    				for ( i = 0; oc->soc_allowed[i] != NULL; i++ ) {
691 					for (a = desc->ad_type; a; a=a->sat_sup) {
692 						if ( a == oc->soc_allowed[i] ) {
693 							return 1;
694 						}
695 					}
696 				}
697 			}
698 
699 		} else {
700 			const char      *text;
701 
702 			/* give it a chance of being retrieved by a proxy... */
703 			(void)slap_bv2undef_ad( &attrs->an_name,
704 				&attrs->an_desc, &text,
705 				SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
706 		}
707 	}
708 
709 	return 0;
710 }
711 
712 
slap_str2undef_ad(const char * str,AttributeDescription ** ad,const char ** text,unsigned flags)713 int slap_str2undef_ad(
714 	const char *str,
715 	AttributeDescription **ad,
716 	const char **text,
717 	unsigned flags )
718 {
719 	struct berval bv;
720 	bv.bv_val = (char *) str;
721 	bv.bv_len = strlen( str );
722 
723 	return slap_bv2undef_ad( &bv, ad, text, flags );
724 }
725 
slap_bv2undef_ad(struct berval * bv,AttributeDescription ** ad,const char ** text,unsigned flags)726 int slap_bv2undef_ad(
727 	struct berval *bv,
728 	AttributeDescription **ad,
729 	const char **text,
730 	unsigned flags )
731 {
732 	AttributeDescription *desc;
733 	AttributeType *at;
734 
735 	assert( ad != NULL );
736 
737 	if( bv == NULL || bv->bv_len == 0 ) {
738 		*text = "empty AttributeDescription";
739 		return LDAP_UNDEFINED_TYPE;
740 	}
741 
742 	/* make sure description is IA5 */
743 	if( ad_keystring( bv ) ) {
744 		*text = "AttributeDescription contains inappropriate characters";
745 		return LDAP_UNDEFINED_TYPE;
746 	}
747 
748 	/* use the appropriate type */
749 	if ( flags & SLAP_AD_PROXIED ) {
750 		at = slap_schema.si_at_proxied;
751 
752 	} else {
753 		at = slap_schema.si_at_undefined;
754 	}
755 
756 	for( desc = at->sat_ad; desc; desc=desc->ad_next ) {
757 		if( desc->ad_cname.bv_len == bv->bv_len &&
758 		    !strcasecmp( desc->ad_cname.bv_val, bv->bv_val ) )
759 		{
760 		    	break;
761 		}
762 	}
763 
764 	if( !desc ) {
765 		if ( flags & SLAP_AD_NOINSERT ) {
766 			*text = NULL;
767 			return LDAP_UNDEFINED_TYPE;
768 		}
769 
770 		desc = ch_malloc(sizeof(AttributeDescription) + 1 +
771 			bv->bv_len);
772 
773 		desc->ad_flags = SLAP_DESC_NONE;
774 		BER_BVZERO( &desc->ad_tags );
775 
776 		desc->ad_cname.bv_len = bv->bv_len;
777 		desc->ad_cname.bv_val = (char *)(desc+1);
778 		strncpy(desc->ad_cname.bv_val, bv->bv_val, bv->bv_len);
779 		desc->ad_cname.bv_val[bv->bv_len] = '\0';
780 
781 		/* canonical to upper case */
782 		ldap_pvt_str2upper( desc->ad_cname.bv_val );
783 
784 		/* shouldn't we protect this for concurrency? */
785 		desc->ad_type = at;
786 		desc->ad_index = 0;
787 		ldap_pvt_thread_mutex_lock( &ad_undef_mutex );
788 		desc->ad_next = desc->ad_type->sat_ad;
789 		desc->ad_type->sat_ad = desc;
790 		ldap_pvt_thread_mutex_unlock( &ad_undef_mutex );
791 
792 		Debug( LDAP_DEBUG_ANY,
793 			"%s attributeDescription \"%s\" inserted.\n",
794 			( flags & SLAP_AD_PROXIED ) ? "PROXIED" : "UNKNOWN",
795 			desc->ad_cname.bv_val );
796 	}
797 
798 	if( !*ad ) {
799 		*ad = desc;
800 	} else {
801 		**ad = *desc;
802 	}
803 
804 	return LDAP_SUCCESS;
805 }
806 
807 AttributeDescription *
slap_bv2tmp_ad(struct berval * bv,void * memctx)808 slap_bv2tmp_ad(
809 	struct berval *bv,
810 	void *memctx )
811 {
812 	AttributeDescription *ad =
813 		 slap_sl_mfuncs.bmf_malloc( sizeof(AttributeDescription) +
814 			bv->bv_len + 1, memctx );
815 
816 	ad->ad_cname.bv_val = (char *)(ad+1);
817 	strncpy( ad->ad_cname.bv_val, bv->bv_val, bv->bv_len+1 );
818 	ad->ad_cname.bv_len = bv->bv_len;
819 	ad->ad_flags = SLAP_DESC_TEMPORARY;
820 	ad->ad_type = slap_schema.si_at_undefined;
821 
822 	return ad;
823 }
824 
825 static int
undef_promote(AttributeType * at,char * name,AttributeType * nat)826 undef_promote(
827 	AttributeType	*at,
828 	char		*name,
829 	AttributeType	*nat )
830 {
831 	AttributeDescription	**u_ad, **n_ad;
832 
833 	/* Get to last ad on the new type */
834 	for ( n_ad = &nat->sat_ad; *n_ad; n_ad = &(*n_ad)->ad_next ) ;
835 
836 	for ( u_ad = &at->sat_ad; *u_ad; ) {
837 		struct berval	bv;
838 
839 		ber_str2bv( name, 0, 0, &bv );
840 
841 		/* remove iff undef == name or undef == name;tag */
842 		if ( (*u_ad)->ad_cname.bv_len >= bv.bv_len
843 			&& strncasecmp( (*u_ad)->ad_cname.bv_val, bv.bv_val, bv.bv_len ) == 0
844 			&& ( (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == '\0'
845 				|| (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == ';' ) )
846 		{
847 			AttributeDescription	*tmp = *u_ad;
848 
849 			*u_ad = (*u_ad)->ad_next;
850 
851 			tmp->ad_type = nat;
852 			tmp->ad_next = NULL;
853 			/* ad_cname was contiguous, no leak here */
854 			tmp->ad_cname = nat->sat_cname;
855 			ldap_pvt_thread_mutex_lock( &ad_index_mutex );
856 			tmp->ad_index = ++ad_count;
857 			ldap_pvt_thread_mutex_unlock( &ad_index_mutex );
858 			*n_ad = tmp;
859 			n_ad = &tmp->ad_next;
860 		} else {
861 			u_ad = &(*u_ad)->ad_next;
862 		}
863 	}
864 
865 	return 0;
866 }
867 
868 int
slap_ad_undef_promote(char * name,AttributeType * at)869 slap_ad_undef_promote(
870 	char *name,
871 	AttributeType *at )
872 {
873 	int	rc;
874 
875 	ldap_pvt_thread_mutex_lock( &ad_undef_mutex );
876 
877 	rc = undef_promote( slap_schema.si_at_undefined, name, at );
878 	if ( rc == 0 ) {
879 		rc = undef_promote( slap_schema.si_at_proxied, name, at );
880 	}
881 
882 	ldap_pvt_thread_mutex_unlock( &ad_undef_mutex );
883 
884 	return rc;
885 }
886 
887 int
an_find(AttributeName * a,struct berval * s)888 an_find(
889     AttributeName *a,
890     struct berval *s
891 )
892 {
893 	if( a == NULL ) return 0;
894 
895 	for ( ; a->an_name.bv_val; a++ ) {
896 		if ( a->an_name.bv_len != s->bv_len) continue;
897 		if ( strcasecmp( s->bv_val, a->an_name.bv_val ) == 0 ) {
898 			return( 1 );
899 		}
900 	}
901 
902 	return( 0 );
903 }
904 
905 /*
906  * Convert a delimited string into a list of AttributeNames; add
907  * on to an existing list if it was given.  If the string is not
908  * a valid attribute name, if a '-' is prepended it is skipped
909  * and the remaining name is tried again; if a '@' (or '+') is
910  * prepended, an objectclass name is searched instead; if a '!'
911  * is prepended, the objectclass name is negated.
912  *
913  * NOTE: currently, if a valid attribute name is not found, the
914  * same string is also checked as valid objectclass name; however,
915  * this behavior is deprecated.
916  */
917 AttributeName *
str2anlist(AttributeName * an,char * in,const char * brkstr)918 str2anlist( AttributeName *an, char *in, const char *brkstr )
919 {
920 	char	*str;
921 	char	*s;
922 	char	*lasts;
923 	int	i, j;
924 	const char *text;
925 	AttributeName *anew;
926 
927 	/* find last element in list */
928 	i = 0;
929 	if ( an != NULL ) {
930 		for ( i = 0; !BER_BVISNULL( &an[ i ].an_name ) ; i++)
931 			;
932 	}
933 
934 	/* protect the input string from strtok */
935 	str = ch_strdup( in );
936 
937 	/* Count words in string */
938 	j = 1;
939 	for ( s = str; *s; s++ ) {
940 		if ( strchr( brkstr, *s ) != NULL ) {
941 			j++;
942 		}
943 	}
944 
945 	an = ch_realloc( an, ( i + j + 1 ) * sizeof( AttributeName ) );
946 	anew = an + i;
947 	for ( s = ldap_pvt_strtok( str, brkstr, &lasts );
948 		s != NULL;
949 		s = ldap_pvt_strtok( NULL, brkstr, &lasts ) )
950 	{
951 		/* put a stop mark */
952 		BER_BVZERO( &anew[1].an_name );
953 
954 		anew->an_desc = NULL;
955 		anew->an_oc = NULL;
956 		anew->an_flags = 0;
957 		ber_str2bv(s, 0, 1, &anew->an_name);
958 		slap_bv2ad(&anew->an_name, &anew->an_desc, &text);
959 		if ( !anew->an_desc ) {
960 			switch( anew->an_name.bv_val[0] ) {
961 			case '-': {
962 					struct berval adname;
963 					adname.bv_len = anew->an_name.bv_len - 1;
964 					adname.bv_val = &anew->an_name.bv_val[1];
965 					slap_bv2ad(&adname, &anew->an_desc, &text);
966 					if ( !anew->an_desc ) {
967 						goto reterr;
968 					}
969 				} break;
970 
971 			case '@':
972 			case '+': /* (deprecated) */
973 			case '!': {
974 					struct berval ocname;
975 					ocname.bv_len = anew->an_name.bv_len - 1;
976 					ocname.bv_val = &anew->an_name.bv_val[1];
977 					anew->an_oc = oc_bvfind( &ocname );
978 					if ( !anew->an_oc ) {
979 						goto reterr;
980 					}
981 
982 					if ( anew->an_name.bv_val[0] == '!' ) {
983 						anew->an_flags |= SLAP_AN_OCEXCLUDE;
984 					}
985 				} break;
986 
987 			default:
988 				/* old (deprecated) way */
989 				anew->an_oc = oc_bvfind( &anew->an_name );
990 				if ( !anew->an_oc ) {
991 					goto reterr;
992 				}
993 			}
994 		}
995 		anew->an_flags |= SLAP_AN_OCINITED;
996 		anew++;
997 	}
998 
999 	BER_BVZERO( &anew->an_name );
1000 	free( str );
1001 	return( an );
1002 
1003 reterr:
1004 	anlist_free( an, 1, NULL );
1005 
1006 	/*
1007 	 * overwrites input string
1008 	 * on error!
1009 	 */
1010 	strcpy( in, s );
1011 	free( str );
1012 	return NULL;
1013 }
1014 
1015 void
anlist_free(AttributeName * an,int freename,void * ctx)1016 anlist_free( AttributeName *an, int freename, void *ctx )
1017 {
1018 	if ( an == NULL ) {
1019 		return;
1020 	}
1021 
1022 	if ( freename ) {
1023 		int	i;
1024 
1025 		for ( i = 0; an[i].an_name.bv_val; i++ ) {
1026 			ber_memfree_x( an[i].an_name.bv_val, ctx );
1027 		}
1028 	}
1029 
1030 	ber_memfree_x( an, ctx );
1031 }
1032 
anlist2charray_x(AttributeName * an,int dup,void * ctx)1033 char **anlist2charray_x( AttributeName *an, int dup, void *ctx )
1034 {
1035     char **attrs;
1036     int i;
1037 
1038     if ( an != NULL ) {
1039         for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ )
1040             ;
1041 		attrs = (char **) slap_sl_malloc( (i + 1) * sizeof(char *), ctx );
1042         for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
1043 			if ( dup )
1044 	            attrs[i] = ch_strdup( an[i].an_name.bv_val );
1045 			else
1046 	            attrs[i] = an[i].an_name.bv_val;
1047         }
1048         attrs[i] = NULL;
1049     } else {
1050         attrs = NULL;
1051     }
1052 
1053     return attrs;
1054 }
1055 
anlist2charray(AttributeName * an,int dup)1056 char **anlist2charray( AttributeName *an, int dup )
1057 {
1058 	return anlist2charray_x( an, dup, NULL );
1059 }
1060 
1061 char**
anlist2attrs(AttributeName * anlist)1062 anlist2attrs( AttributeName * anlist )
1063 {
1064 	int i, j, k = 0;
1065 	int n;
1066 	char **attrs;
1067 	ObjectClass *oc;
1068 
1069 	if ( anlist == NULL )
1070 		return NULL;
1071 
1072 	for ( i = 0; anlist[i].an_name.bv_val; i++ ) {
1073 		if ( ( oc = anlist[i].an_oc ) ) {
1074 			for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) ;
1075 			k += j;
1076 			for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) ;
1077 			k += j;
1078 		}
1079 	}
1080 
1081 	if ( i == 0 )
1082 		return NULL;
1083 
1084 	attrs = anlist2charray( anlist, 1 );
1085 
1086 	n = i;
1087 
1088 	if ( k )
1089 		attrs = (char **) ch_realloc( attrs, (i + k + 1) * sizeof( char * ));
1090 
1091    	for ( i = 0; anlist[i].an_name.bv_val; i++ ) {
1092 		if ( ( oc = anlist[i].an_oc ) ) {
1093 			for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) {
1094 				attrs[n++] = ch_strdup(
1095 								oc->soc_required[j]->sat_cname.bv_val );
1096 			}
1097 			for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) {
1098 				attrs[n++] = ch_strdup(
1099 								oc->soc_allowed[j]->sat_cname.bv_val );
1100 			}
1101 		}
1102 	}
1103 
1104 	if ( attrs )
1105 		attrs[n] = NULL;
1106 
1107 	i = 0;
1108 	while ( attrs && attrs[i] ) {
1109 		if ( *attrs[i] == '@' ) {
1110 			ch_free( attrs[i] );
1111 			for ( j = i; attrs[j]; j++ ) {
1112 				attrs[j] = attrs[j+1];
1113 			}
1114 		} else {
1115 			i++;
1116 		}
1117 	}
1118 
1119 	for ( i = 0; attrs && attrs[i]; i++ ) {
1120 		j = i + 1;
1121 		while ( attrs && attrs[j] ) {
1122 			if ( !strcmp( attrs[i], attrs[j] )) {
1123 				ch_free( attrs[j] );
1124 				for ( k = j; attrs && attrs[k]; k++ ) {
1125 					attrs[k] = attrs[k+1];
1126 				}
1127 			} else {
1128 				j++;
1129 			}
1130 		}
1131 	}
1132 
1133 	if ( i != n )
1134 		attrs = (char **) ch_realloc( attrs, (i+1) * sizeof( char * ));
1135 
1136 	return attrs;
1137 }
1138 
1139 #define LBUFSIZ	80
1140 AttributeName*
file2anlist(AttributeName * an,const char * fname,const char * brkstr)1141 file2anlist( AttributeName *an, const char *fname, const char *brkstr )
1142 {
1143 	FILE	*fp;
1144 	char	*line = NULL;
1145 	char	*lcur = NULL;
1146 	char	*c;
1147 	size_t	lmax = LBUFSIZ;
1148 
1149 	fp = fopen( fname, "r" );
1150 	if ( fp == NULL ) {
1151 		char ebuf[128];
1152 		int saved_errno = errno;
1153 		Debug( LDAP_DEBUG_ANY,
1154 			"get_attrs_from_file: failed to open attribute list file "
1155 			"\"%s\": %s\n", fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ) );
1156 		return NULL;
1157 	}
1158 
1159 	lcur = line = (char *) ch_malloc( lmax );
1160 	if ( !line ) {
1161 		Debug( LDAP_DEBUG_ANY,
1162 			"get_attrs_from_file: could not allocate memory\n" );
1163 		fclose(fp);
1164 		return NULL;
1165 	}
1166 
1167 	while ( fgets( lcur, LBUFSIZ, fp ) != NULL ) {
1168 		if ( ( c = strchr( lcur, '\n' ) ) ) {
1169 			if ( c == line ) {
1170 				*c = '\0';
1171 			} else if ( *(c-1) == '\r' ) {
1172 				*(c-1) = '\0';
1173 			} else {
1174 				*c = '\0';
1175 			}
1176 		} else {
1177 			lmax += LBUFSIZ;
1178 			line = (char *) ch_realloc( line, lmax );
1179 			if ( !line ) {
1180 				Debug( LDAP_DEBUG_ANY,
1181 					"get_attrs_from_file: could not allocate memory\n" );
1182 				fclose(fp);
1183 				return NULL;
1184 			}
1185 			lcur = line + strlen( line );
1186 			continue;
1187 		}
1188 		an = str2anlist( an, line, brkstr );
1189 		if ( an == NULL )
1190 			break;
1191 		lcur = line;
1192 	}
1193 	ch_free( line );
1194 	fclose(fp);
1195 	return an;
1196 }
1197 #undef LBUFSIZ
1198 
1199 /* Define an attribute option. */
1200 int
ad_define_option(const char * name,const char * fname,int lineno)1201 ad_define_option( const char *name, const char *fname, int lineno )
1202 {
1203 	int i;
1204 	unsigned int optlen;
1205 
1206 	if ( options == &lang_option ) {
1207 		options = NULL;
1208 		option_count = 0;
1209 	}
1210 	if ( name == NULL )
1211 		return 0;
1212 
1213 	optlen = 0;
1214 	do {
1215 		if ( !DESC_CHAR( name[optlen] ) ) {
1216 			/* allow trailing '=', same as '-' */
1217 			if ( name[optlen] == '=' && !name[optlen+1] ) {
1218 				msad_range_hack = 1;
1219 				continue;
1220 			}
1221 			Debug( LDAP_DEBUG_ANY,
1222 			       "%s: line %d: illegal option name \"%s\"\n",
1223 				    fname, lineno, name );
1224 			return 1;
1225 		}
1226 	} while ( name[++optlen] );
1227 
1228 	options = ch_realloc( options,
1229 		(option_count+1) * sizeof(Attr_option) );
1230 
1231 	if ( strcasecmp( name, "binary" ) == 0
1232 	     || ad_find_option_definition( name, optlen ) ) {
1233 		Debug( LDAP_DEBUG_ANY,
1234 		       "%s: line %d: option \"%s\" is already defined\n",
1235 		       fname, lineno, name );
1236 		return 1;
1237 	}
1238 
1239 	for ( i = option_count; i; --i ) {
1240 		if ( strcasecmp( name, options[i-1].name.bv_val ) >= 0 )
1241 			break;
1242 		options[i] = options[i-1];
1243 	}
1244 
1245 	options[i].name.bv_val = ch_strdup( name );
1246 	options[i].name.bv_len = optlen;
1247 	options[i].prefix = (name[optlen-1] == '-') ||
1248  		(name[optlen-1] == '=');
1249 
1250 	if ( i != option_count &&
1251 	     options[i].prefix &&
1252 	     optlen < options[i+1].name.bv_len &&
1253 	     strncasecmp( name, options[i+1].name.bv_val, optlen ) == 0 ) {
1254 			Debug( LDAP_DEBUG_ANY,
1255 			       "%s: line %d: option \"%s\" overrides previous option\n",
1256 				    fname, lineno, name );
1257 			return 1;
1258 	}
1259 
1260 	option_count++;
1261 	return 0;
1262 }
1263 
1264 void
ad_unparse_options(BerVarray * res)1265 ad_unparse_options( BerVarray *res )
1266 {
1267 	int i;
1268 	for ( i = 0; i < option_count; i++ ) {
1269 		value_add_one( res, &options[i].name );
1270 	}
1271 }
1272 
1273 /* Find the definition of the option name or prefix matching the arguments */
1274 static Attr_option *
ad_find_option_definition(const char * opt,int optlen)1275 ad_find_option_definition( const char *opt, int optlen )
1276 {
1277 	int top = 0, bot = option_count;
1278 	while ( top < bot ) {
1279 		int mid = (top + bot) / 2;
1280 		int mlen = options[mid].name.bv_len;
1281 		char *mname = options[mid].name.bv_val;
1282 		int j;
1283 		if ( optlen < mlen ) {
1284 			j = strncasecmp( opt, mname, optlen ) - 1;
1285 		} else {
1286 			j = strncasecmp( opt, mname, mlen );
1287 			if ( j==0 && (optlen==mlen || options[mid].prefix) )
1288 				return &options[mid];
1289 		}
1290 		if ( j < 0 )
1291 			bot = mid;
1292 		else
1293 			top = mid + 1;
1294 	}
1295 	return NULL;
1296 }
1297 
ad_mr(AttributeDescription * ad,unsigned usage)1298 MatchingRule *ad_mr(
1299 	AttributeDescription *ad,
1300 	unsigned usage )
1301 {
1302 	switch( usage & SLAP_MR_TYPE_MASK ) {
1303 	case SLAP_MR_NONE:
1304 	case SLAP_MR_EQUALITY:
1305 		return ad->ad_type->sat_equality;
1306 		break;
1307 	case SLAP_MR_ORDERING:
1308 		return ad->ad_type->sat_ordering;
1309 		break;
1310 	case SLAP_MR_SUBSTR:
1311 		return ad->ad_type->sat_substr;
1312 		break;
1313 	case SLAP_MR_EXT:
1314 	default:
1315 		assert( 0 /* ad_mr: bad usage */);
1316 	}
1317 	return NULL;
1318 }
1319