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