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