1 /*        $NetBSD: acl.c,v 1.3 2021/08/14 16:14:58 christos Exp $     */
2 
3 /* acl.c - routines to parse and check acl's */
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 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
19  * All rights reserved.
20  *
21  * Redistribution and use in source and binary forms are permitted
22  * provided that this notice is preserved and that due credit is given
23  * to the University of Michigan at Ann Arbor. The name of the University
24  * may not be used to endorse or promote products derived from this
25  * software without specific prior written permission. This software
26  * is provided ``as is'' without express or implied warranty.
27  */
28 
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: acl.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
31 
32 #include "portable.h"
33 
34 #include <stdio.h>
35 
36 #include <ac/regex.h>
37 #include <ac/socket.h>
38 #include <ac/string.h>
39 
40 #include "slap.h"
41 #include "sets.h"
42 #include "lber_pvt.h"
43 #include "lutil.h"
44 
45 #define ACL_BUF_SIZE          1024      /* use most appropriate size */
46 
47 static const struct berval    acl_bv_ip_eq = BER_BVC( "IP=" );
48 #ifdef LDAP_PF_INET6
49 static const struct berval    acl_bv_ipv6_eq = BER_BVC( "IP=[" );
50 #endif /* LDAP_PF_INET6 */
51 #ifdef LDAP_PF_LOCAL
52 static const struct berval    acl_bv_path_eq = BER_BVC("PATH=");
53 #endif /* LDAP_PF_LOCAL */
54 
55 static AccessControl * slap_acl_get(
56           AccessControl *ac, int *count,
57           Operation *op, Entry *e,
58           AttributeDescription *desc,
59           struct berval *val,
60           AclRegexMatches *matches,
61           slap_mask_t *mask,
62           AccessControlState *state );
63 
64 static slap_control_t slap_acl_mask(
65           AccessControl *ac,
66           AccessControl *prev,
67           slap_mask_t *mask,
68           Operation *op, Entry *e,
69           AttributeDescription *desc,
70           struct berval *val,
71           AclRegexMatches *matches,
72           int count,
73           AccessControlState *state,
74           slap_access_t access );
75 
76 static int          regex_matches(
77           struct berval *pat, char *str,
78           struct berval *dn_matches, struct berval *val_matches,
79           AclRegexMatches *matches);
80 
81 typedef   struct AclSetCookie {
82           SetCookie asc_cookie;
83 #define   asc_op              asc_cookie.set_op
84           Entry               *asc_e;
85 } AclSetCookie;
86 
87 
88 SLAP_SET_GATHER acl_set_gather;
89 SLAP_SET_GATHER acl_set_gather2;
90 
91 /*
92  * access_allowed - check whether op->o_ndn is allowed the requested access
93  * to entry e, attribute attr, value val.  if val is null, access to
94  * the whole attribute is assumed (all values).
95  *
96  * This routine loops through all access controls and calls
97  * slap_acl_mask() on each applicable access control.
98  * The loop exits when a definitive answer is reached or
99  * or no more controls remain.
100  *
101  * returns:
102  *                  0         access denied
103  *                  1         access granted
104  *
105  * Notes:
106  * - can be legally called with op == NULL
107  * - can be legally called with op->o_bd == NULL
108  */
109 
110 int
slap_access_always_allowed(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,slap_access_t access,AccessControlState * state,slap_mask_t * maskp)111 slap_access_always_allowed(
112           Operation           *op,
113           Entry                         *e,
114           AttributeDescription          *desc,
115           struct berval                 *val,
116           slap_access_t                 access,
117           AccessControlState  *state,
118           slap_mask_t                   *maskp )
119 {
120           assert( maskp != NULL );
121 
122           /* assign all */
123           ACL_LVL_ASSIGN_MANAGE( *maskp );
124 
125           return 1;
126 }
127 
128 #define MATCHES_DNMAXCOUNT(m)                                                   \
129           ( sizeof ( (m)->dn_data ) / sizeof( *(m)->dn_data ) )
130 #define MATCHES_VALMAXCOUNT(m)                                                  \
131           ( sizeof ( (m)->val_data ) / sizeof( *(m)->val_data ) )
132 #define MATCHES_MEMSET(m) do {                                                  \
133           memset( (m)->dn_data, '\0', sizeof( (m)->dn_data ) );       \
134           memset( (m)->val_data, '\0', sizeof( (m)->val_data ) );     \
135           (m)->dn_count = MATCHES_DNMAXCOUNT( (m) );                  \
136           (m)->val_count = MATCHES_VALMAXCOUNT( (m) );                \
137 } while ( 0 /* CONSTCOND */ )
138 
139 int
slap_access_allowed(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,slap_access_t access,AccessControlState * state,slap_mask_t * maskp)140 slap_access_allowed(
141           Operation           *op,
142           Entry                         *e,
143           AttributeDescription          *desc,
144           struct berval                 *val,
145           slap_access_t                 access,
146           AccessControlState  *state,
147           slap_mask_t                   *maskp )
148 {
149           int                                     ret = 1;
150           int                                     count;
151           AccessControl                           *a, *prev;
152 
153 #ifdef LDAP_DEBUG
154           char                                    accessmaskbuf[ACCESSMASK_MAXLEN];
155 #endif
156           slap_mask_t                             mask;
157           slap_control_t                          control;
158           slap_access_t                           access_level;
159           const char                              *attr;
160           AclRegexMatches                         matches;
161           AccessControlState            acl_state = ACL_STATE_INIT;
162           static AccessControlState     state_init = ACL_STATE_INIT;
163 
164           assert( op != NULL );
165           assert( e != NULL );
166           assert( desc != NULL );
167           assert( maskp != NULL );
168 
169           access_level = ACL_LEVEL( access );
170           attr = desc->ad_cname.bv_val;
171 
172           assert( attr != NULL );
173 
174           ACL_INIT( mask );
175 
176           /* grant database root access */
177           if ( be_isroot( op ) ) {
178                     Debug( LDAP_DEBUG_ACL, "<= root access granted\n" );
179                     mask = ACL_LVL_MANAGE;
180                     goto done;
181           }
182 
183           /*
184            * no-user-modification operational attributes are ignored
185            * by ACL_WRITE checking as any found here are not provided
186            * by the user
187            *
188            * NOTE: but they are not ignored for ACL_MANAGE, because
189            * if we get here it means a non-root user is trying to
190            * manage data, so we need to check its privileges.
191            */
192           if ( access_level == ACL_WRITE_
193                     && is_at_no_user_mod( desc->ad_type )
194                     && desc != slap_schema.si_ad_entry
195                     && desc != slap_schema.si_ad_children )
196           {
197                     Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
198                               " %s access granted\n",
199                               attr );
200                     goto done;
201           }
202 
203           /* use backend default access if no backend acls */
204           if ( op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
205                     int       i;
206 
207                     Debug( LDAP_DEBUG_ACL,
208                               "=> slap_access_allowed: backend default %s "
209                               "access %s to \"%s\"\n",
210                               access2str( access ),
211                               op->o_bd->be_dfltaccess >= access_level ? "granted" : "denied",
212                               op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
213                     ret = op->o_bd->be_dfltaccess >= access_level;
214 
215                     mask = ACL_PRIV_LEVEL;
216                     for ( i = ACL_NONE; i <= op->o_bd->be_dfltaccess; i++ ) {
217                               ACL_PRIV_SET( mask, ACL_ACCESS2PRIV( i ) );
218                     }
219 
220                     goto done;
221           }
222 
223           ret = 0;
224           control = ACL_BREAK;
225 
226           if ( state == NULL )
227                     state = &acl_state;
228           if ( state->as_desc == desc &&
229                     state->as_access == access &&
230                     state->as_vd_acl_present )
231           {
232                     a = state->as_vd_acl;
233                     count = state->as_vd_acl_count;
234                     if ( state->as_fe_done )
235                               state->as_fe_done--;
236                     ACL_PRIV_ASSIGN( mask, state->as_vd_mask );
237           } else {
238                     *state = state_init;
239 
240                     a = NULL;
241                     count = 0;
242                     ACL_PRIV_ASSIGN( mask, *maskp );
243           }
244 
245           MATCHES_MEMSET( &matches );
246           prev = a;
247 
248           while ( ( a = slap_acl_get( a, &count, op, e, desc, val,
249                     &matches, &mask, state ) ) != NULL )
250           {
251                     int i;
252                     int dnmaxcount = MATCHES_DNMAXCOUNT( &matches );
253                     int valmaxcount = MATCHES_VALMAXCOUNT( &matches );
254                     regmatch_t *dn_data = matches.dn_data;
255                     regmatch_t *val_data = matches.val_data;
256 
257                     /* DN matches */
258                     for ( i = 0; i < dnmaxcount && dn_data[i].rm_eo > 0; i++ ) {
259                               char *data = e->e_ndn;
260 
261                               Debug( LDAP_DEBUG_ACL, "=> match[dn%d]: %d %d ", i,
262                                         (int)dn_data[i].rm_so,
263                                         (int)dn_data[i].rm_eo );
264                               if ( dn_data[i].rm_so <= dn_data[0].rm_eo ) {
265                                         int n;
266                                         for ( n = dn_data[i].rm_so;
267                                               n < dn_data[i].rm_eo; n++ ) {
268                                                   Debug( LDAP_DEBUG_ACL, "%c",
269                                                          data[n] );
270                                         }
271                               }
272                               Debug( LDAP_DEBUG_ACL, "\n" );
273                     }
274 
275                     /* val matches */
276                     for ( i = 0; i < valmaxcount && val_data[i].rm_eo > 0; i++ ) {
277                               char *data = val->bv_val;
278 
279                               Debug( LDAP_DEBUG_ACL, "=> match[val%d]: %d %d ", i,
280                                         (int)val_data[i].rm_so,
281                                         (int)val_data[i].rm_eo );
282                               if ( val_data[i].rm_so <= val_data[0].rm_eo ) {
283                                         int n;
284                                         for ( n = val_data[i].rm_so;
285                                               n < val_data[i].rm_eo; n++ ) {
286                                                   Debug( LDAP_DEBUG_ACL, "%c",
287                                                          data[n] );
288                                         }
289                               }
290                               Debug( LDAP_DEBUG_ACL, "\n" );
291                     }
292 
293                     control = slap_acl_mask( a, prev, &mask, op,
294                               e, desc, val, &matches, count, state, access );
295 
296                     if ( control != ACL_BREAK ) {
297                               break;
298                     }
299 
300                     MATCHES_MEMSET( &matches );
301                     prev = a;
302           }
303 
304           if ( ACL_IS_INVALID( mask ) ) {
305                     Debug( LDAP_DEBUG_ACL,
306                               "=> slap_access_allowed: \"%s\" (%s) invalid!\n",
307                               e->e_dn, attr );
308                     ACL_PRIV_ASSIGN( mask, *maskp );
309 
310           } else if ( control == ACL_BREAK ) {
311                     Debug( LDAP_DEBUG_ACL,
312                               "=> slap_access_allowed: no more rules\n" );
313 
314                     goto done;
315           }
316 
317           ret = ACL_GRANT( mask, access );
318 
319           Debug( LDAP_DEBUG_ACL,
320                     "=> slap_access_allowed: %s access %s by %s\n",
321                     access2str( access ), ret ? "granted" : "denied",
322                     accessmask2str( mask, accessmaskbuf, 1 ) );
323 
324 done:
325           ACL_PRIV_ASSIGN( *maskp, mask );
326           return ret;
327 }
328 
329 int
fe_access_allowed(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,slap_access_t access,AccessControlState * state,slap_mask_t * maskp)330 fe_access_allowed(
331           Operation           *op,
332           Entry                         *e,
333           AttributeDescription          *desc,
334           struct berval                 *val,
335           slap_access_t                 access,
336           AccessControlState  *state,
337           slap_mask_t                   *maskp )
338 {
339           BackendDB           *be_orig;
340           int                           rc;
341 
342           /*
343            * NOTE: control gets here if FIXME
344            * if an appropriate backend cannot be selected for the operation,
345            * we assume that the frontend should handle this
346            * FIXME: should select_backend() take care of this,
347            * and return frontendDB instead of NULL?  maybe for some value
348            * of the flags?
349            */
350           be_orig = op->o_bd;
351 
352           if ( op->o_bd == NULL ) {
353                     op->o_bd = select_backend( &op->o_req_ndn, 0 );
354                     if ( op->o_bd == NULL )
355                               op->o_bd = frontendDB;
356           }
357           rc = slap_access_allowed( op, e, desc, val, access, state, maskp );
358           op->o_bd = be_orig;
359 
360           return rc;
361 }
362 
363 int
access_allowed_mask(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,slap_access_t access,AccessControlState * state,slap_mask_t * maskp)364 access_allowed_mask(
365           Operation           *op,
366           Entry                         *e,
367           AttributeDescription          *desc,
368           struct berval                 *val,
369           slap_access_t                 access,
370           AccessControlState  *state,
371           slap_mask_t                   *maskp )
372 {
373           int                                     ret = 1;
374           int                                     be_null = 0;
375 
376 #ifdef LDAP_DEBUG
377           char                                    accessmaskbuf[ACCESSMASK_MAXLEN];
378 #endif
379           slap_mask_t                             mask;
380           slap_access_t                           access_level;
381           const char                              *attr;
382 
383           assert( e != NULL );
384           assert( desc != NULL );
385 
386           access_level = ACL_LEVEL( access );
387 
388           assert( access_level > ACL_NONE );
389 
390           ACL_INIT( mask );
391           if ( maskp ) ACL_INVALIDATE( *maskp );
392 
393           attr = desc->ad_cname.bv_val;
394 
395           assert( attr != NULL );
396 
397           if ( op ) {
398                     if ( op->o_acl_priv != ACL_NONE ) {
399                               access = op->o_acl_priv;
400 
401                     } else if ( op->o_is_auth_check &&
402                               ( access_level == ACL_SEARCH || access_level == ACL_READ ) )
403                     {
404                               access = ACL_AUTH;
405 
406                     } else if ( get_relax( op ) && access_level == ACL_WRITE_ &&
407                               desc == slap_schema.si_ad_entry )
408                     {
409                               access = ACL_MANAGE;
410                     }
411           }
412 
413           if ( state != NULL ) {
414                     if ( state->as_desc == desc &&
415                               state->as_access == access &&
416                               state->as_result != -1 &&
417                               !state->as_vd_acl_present )
418                               {
419                               Debug( LDAP_DEBUG_ACL,
420                                         "=> access_allowed: result was in cache (%s)\n",
421                                         attr );
422                                         return state->as_result;
423                     } else {
424                               Debug( LDAP_DEBUG_ACL,
425                                         "=> access_allowed: result not in cache (%s)\n",
426                                         attr );
427                     }
428           }
429 
430           Debug( LDAP_DEBUG_ACL,
431                     "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
432                     access2str( access ), e->e_dn, attr );
433 
434           if ( op == NULL ) {
435                     /* no-op call */
436                     goto done;
437           }
438 
439           if ( op->o_bd == NULL ) {
440                     op->o_bd = LDAP_STAILQ_FIRST( &backendDB );
441                     be_null = 1;
442 
443                     /* FIXME: experimental; use first backend rules
444                      * iff there is no global_acl (ITS#3100)
445                      */
446                     if ( frontendDB->be_acl != NULL ) {
447                               op->o_bd = frontendDB;
448                     }
449           }
450           assert( op->o_bd != NULL );
451 
452           /* this is enforced in backend_add() */
453           if ( op->o_bd->bd_info->bi_access_allowed ) {
454                     /* delegate to backend */
455                     ret = op->o_bd->bd_info->bi_access_allowed( op, e,
456                                         desc, val, access, state, &mask );
457 
458           } else {
459                     /* use default (but pass through frontend
460                      * for global ACL overlays) */
461                     ret = frontendDB->bd_info->bi_access_allowed( op, e,
462                                         desc, val, access, state, &mask );
463           }
464 
465           if ( !ret ) {
466                     if ( ACL_IS_INVALID( mask ) ) {
467                               Debug( LDAP_DEBUG_ACL,
468                                         "=> access_allowed: \"%s\" (%s) invalid!\n",
469                                         e->e_dn, attr );
470                               ACL_INIT( mask );
471 
472                     } else {
473                               Debug( LDAP_DEBUG_ACL,
474                                         "=> access_allowed: no more rules\n" );
475 
476                               goto done;
477                     }
478           }
479 
480           Debug( LDAP_DEBUG_ACL,
481                     "=> access_allowed: %s access %s by %s\n",
482                     access2str( access ), ret ? "granted" : "denied",
483                     accessmask2str( mask, accessmaskbuf, 1 ) );
484 
485 done:
486           if ( state != NULL ) {
487                     state->as_access = access;
488                               state->as_result = ret;
489                     state->as_desc = desc;
490           }
491           if ( be_null ) op->o_bd = NULL;
492           if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask );
493           return ret;
494 }
495 
496 
497 /*
498  * slap_acl_get - return the acl applicable to entry e, attribute
499  * attr.  the acl returned is suitable for use in subsequent calls to
500  * acl_access_allowed().
501  */
502 
503 static AccessControl *
slap_acl_get(AccessControl * a,int * count,Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,AclRegexMatches * matches,slap_mask_t * mask,AccessControlState * state)504 slap_acl_get(
505           AccessControl *a,
506           int                           *count,
507           Operation *op,
508           Entry               *e,
509           AttributeDescription *desc,
510           struct berval       *val,
511           AclRegexMatches     *matches,
512           slap_mask_t *mask,
513           AccessControlState *state )
514 {
515           const char *attr;
516           ber_len_t dnlen;
517           AccessControl *prev;
518 
519           assert( e != NULL );
520           assert( count != NULL );
521           assert( desc != NULL );
522           assert( state != NULL );
523 
524           attr = desc->ad_cname.bv_val;
525 
526           assert( attr != NULL );
527 
528           if( a == NULL ) {
529                     if( op->o_bd == NULL || op->o_bd->be_acl == NULL ) {
530                               a = frontendDB->be_acl;
531                     } else {
532                               a = op->o_bd->be_acl;
533                     }
534                     prev = NULL;
535 
536                     assert( a != NULL );
537                     if ( a == frontendDB->be_acl )
538                               state->as_fe_done = 1;
539           } else {
540                     prev = a;
541                     a = a->acl_next;
542           }
543 
544           dnlen = e->e_nname.bv_len;
545 
546  retry:
547           for ( ; a != NULL; prev = a, a = a->acl_next ) {
548                     (*count) ++;
549 
550                     if ( a != frontendDB->be_acl && state->as_fe_done )
551                               state->as_fe_done++;
552 
553                     if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) {
554                               if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
555                                         Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n",
556                                                   *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
557                                         if ( regexec ( &a->acl_dn_re,
558                                                          e->e_ndn,
559                                                          matches->dn_count,
560                                                          matches->dn_data, 0 ) )
561                                                   continue;
562 
563                               } else {
564                                         ber_len_t patlen;
565 
566                                         Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n",
567                                                   *count, a->acl_dn_pat.bv_val );
568                                         patlen = a->acl_dn_pat.bv_len;
569                                         if ( dnlen < patlen )
570                                                   continue;
571 
572                                         if ( a->acl_dn_style == ACL_STYLE_BASE ) {
573                                                   /* base dn -- entire object DN must match */
574                                                   if ( dnlen != patlen )
575                                                             continue;
576 
577                                         } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
578                                                   ber_len_t rdnlen = 0;
579                                                   ber_len_t sep = 0;
580 
581                                                   if ( dnlen <= patlen )
582                                                             continue;
583 
584                                                   if ( patlen > 0 ) {
585                                                             if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
586                                                                       continue;
587                                                             sep = 1;
588                                                   }
589 
590                                                   rdnlen = dn_rdnlen( NULL, &e->e_nname );
591                                                   if ( rdnlen + patlen + sep != dnlen )
592                                                             continue;
593 
594                                         } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
595                                                   if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
596                                                             continue;
597 
598                                         } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
599                                                   if ( dnlen <= patlen )
600                                                             continue;
601                                                   if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
602                                                             continue;
603                                         }
604 
605                                         if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
606                                                   continue;
607                               }
608 
609                               Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
610                                         *count );
611                     }
612 
613                     if ( a->acl_attrs && !ad_inlist( desc, a->acl_attrs ) ) {
614                               matches->dn_data[0].rm_so = -1;
615                               matches->dn_data[0].rm_eo = -1;
616                               matches->val_data[0].rm_so = -1;
617                               matches->val_data[0].rm_eo = -1;
618                               continue;
619                     }
620 
621                     /* Is this ACL only for a specific value? */
622                     if ( a->acl_attrval.bv_val ) {
623                               if ( val == NULL ) {
624                                         continue;
625                               }
626 
627                               if ( !state->as_vd_acl_present ) {
628                                         state->as_vd_acl_present = 1;
629                                         state->as_vd_acl = prev;
630                                         state->as_vd_acl_count = *count - 1;
631                                         ACL_PRIV_ASSIGN ( state->as_vd_mask, *mask );
632                               }
633 
634                               if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
635                                         Debug( LDAP_DEBUG_ACL,
636                                                   "acl_get: valpat %s\n",
637                                                   a->acl_attrval.bv_val );
638                                         if ( regexec ( &a->acl_attrval_re,
639                                                                 val->bv_val,
640                                                                 matches->val_count,
641                                                                 matches->val_data, 0 ) )
642                                         {
643                                                   continue;
644                                         }
645 
646                               } else {
647                                         int match = 0;
648                                         const char *text;
649                                         Debug( LDAP_DEBUG_ACL,
650                                                   "acl_get: val %s\n",
651                                                   a->acl_attrval.bv_val );
652 
653                                         if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
654                                                   if (value_match( &match, desc,
655                                                             a->acl_attrval_mr, 0,
656                                                             val, &a->acl_attrval, &text ) != LDAP_SUCCESS ||
657                                                                       match )
658                                                             continue;
659 
660                                         } else {
661                                                   ber_len_t patlen, vdnlen;
662 
663                                                   patlen = a->acl_attrval.bv_len;
664                                                   vdnlen = val->bv_len;
665 
666                                                   if ( vdnlen < patlen )
667                                                             continue;
668 
669                                                   if ( a->acl_attrval_style == ACL_STYLE_BASE ) {
670                                                             if ( vdnlen > patlen )
671                                                                       continue;
672 
673                                                   } else if ( a->acl_attrval_style == ACL_STYLE_ONE ) {
674                                                             ber_len_t rdnlen = 0;
675 
676                                                             if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
677                                                                       continue;
678 
679                                                             rdnlen = dn_rdnlen( NULL, val );
680                                                             if ( rdnlen + patlen + 1 != vdnlen )
681                                                                       continue;
682 
683                                                   } else if ( a->acl_attrval_style == ACL_STYLE_SUBTREE ) {
684                                                             if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
685                                                                       continue;
686 
687                                                   } else if ( a->acl_attrval_style == ACL_STYLE_CHILDREN ) {
688                                                             if ( vdnlen <= patlen )
689                                                                       continue;
690 
691                                                             if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
692                                                                       continue;
693                                                   }
694 
695                                                   if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ) )
696                                                             continue;
697                                         }
698                               }
699                     }
700 
701                     if ( a->acl_filter != NULL ) {
702                               ber_int_t rc = test_filter( NULL, e, a->acl_filter );
703                               if ( rc != LDAP_COMPARE_TRUE ) {
704                                         continue;
705                               }
706                     }
707 
708                     Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n",
709                            *count, attr );
710                     return a;
711           }
712 
713           if ( !state->as_fe_done ) {
714                     state->as_fe_done = 1;
715                     a = frontendDB->be_acl;
716                     goto retry;
717           }
718 
719           Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n" );
720           return( NULL );
721 }
722 
723 /*
724  * Record value-dependent access control state
725  */
726 #define ACL_RECORD_VALUE_STATE do { \
727                     if( state && !state->as_vd_acl_present ) { \
728                               state->as_vd_acl_present = 1; \
729                               state->as_vd_acl = prev; \
730                               state->as_vd_acl_count = count - 1; \
731                               ACL_PRIV_ASSIGN( state->as_vd_mask, *mask ); \
732                     } \
733           } while( 0 )
734 
735 static int
acl_mask_dn(Operation * op,Entry * e,struct berval * val,AccessControl * a,AclRegexMatches * matches,slap_dn_access * bdn,struct berval * opndn)736 acl_mask_dn(
737           Operation           *op,
738           Entry                         *e,
739           struct berval                 *val,
740           AccessControl                 *a,
741           AclRegexMatches               *matches,
742           slap_dn_access                *bdn,
743           struct berval                 *opndn )
744 {
745           /*
746            * if access applies to the entry itself, and the
747            * user is bound as somebody in the same namespace as
748            * the entry, OR the given dn matches the dn pattern
749            */
750           /*
751            * NOTE: styles "anonymous", "users" and "self"
752            * have been moved to enum slap_style_t, whose
753            * value is set in a_dn_style; however, the string
754            * is maintained in a_dn_pat.
755            */
756 
757           if ( bdn->a_style == ACL_STYLE_ANONYMOUS ) {
758                     if ( !BER_BVISEMPTY( opndn ) ) {
759                               return 1;
760                     }
761 
762           } else if ( bdn->a_style == ACL_STYLE_USERS ) {
763                     if ( BER_BVISEMPTY( opndn ) ) {
764                               return 1;
765                     }
766 
767           } else if ( bdn->a_style == ACL_STYLE_SELF ) {
768                     struct berval       ndn, selfndn;
769                     int                 level;
770 
771                     if ( BER_BVISEMPTY( opndn ) || BER_BVISNULL( &e->e_nname ) ) {
772                               return 1;
773                     }
774 
775                     level = bdn->a_self_level;
776                     if ( level < 0 ) {
777                               selfndn = *opndn;
778                               ndn = e->e_nname;
779                               level = -level;
780 
781                     } else {
782                               ndn = *opndn;
783                               selfndn = e->e_nname;
784                     }
785 
786                     for ( ; level > 0; level-- ) {
787                               if ( BER_BVISEMPTY( &ndn ) ) {
788                                         break;
789                               }
790                               dnParent( &ndn, &ndn );
791                     }
792 
793                     if ( BER_BVISEMPTY( &ndn ) || !dn_match( &ndn, &selfndn ) )
794                     {
795                               return 1;
796                     }
797 
798           } else if ( bdn->a_style == ACL_STYLE_REGEX ) {
799                     if ( !ber_bvccmp( &bdn->a_pat, '*' ) ) {
800                               AclRegexMatches     tmp_matches,
801                                                   *tmp_matchesp = &tmp_matches;
802                               int                 rc = 0;
803                               regmatch_t          *tmp_data;
804 
805                               MATCHES_MEMSET( &tmp_matches );
806                               tmp_data = &tmp_matches.dn_data[0];
807 
808                               if ( a->acl_attrval_style == ACL_STYLE_REGEX )
809                                         tmp_matchesp = matches;
810                               else switch ( a->acl_dn_style ) {
811                               case ACL_STYLE_REGEX:
812                                         if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
813                                                   tmp_matchesp = matches;
814                                                   break;
815                                         }
816                               /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
817 
818                               case ACL_STYLE_BASE:
819                                         tmp_data[0].rm_so = 0;
820                                         tmp_data[0].rm_eo = e->e_nname.bv_len;
821                                         tmp_matches.dn_count = 1;
822                                         break;
823 
824                               case ACL_STYLE_ONE:
825                               case ACL_STYLE_SUBTREE:
826                               case ACL_STYLE_CHILDREN:
827                                         tmp_data[0].rm_so = 0;
828                                         tmp_data[0].rm_eo = e->e_nname.bv_len;
829                                         tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
830                                         tmp_data[1].rm_eo = e->e_nname.bv_len;
831                                         tmp_matches.dn_count = 2;
832                                         break;
833 
834                               default:
835                                         /* error */
836                                         rc = 1;
837                                         break;
838                               }
839 
840                               if ( rc ) {
841                                         return 1;
842                               }
843 
844                               if ( !regex_matches( &bdn->a_pat, opndn->bv_val,
845                                         &e->e_nname, NULL, tmp_matchesp ) )
846                               {
847                                         return 1;
848                               }
849                     }
850 
851           } else {
852                     struct berval       pat;
853                     ber_len_t patlen, odnlen;
854                     int                 got_match = 0;
855 
856                     if ( e->e_dn == NULL )
857                               return 1;
858 
859                     if ( bdn->a_expand ) {
860                               struct berval       bv;
861                               char                buf[ACL_BUF_SIZE];
862 
863                               AclRegexMatches     tmp_matches,
864                                                   *tmp_matchesp = &tmp_matches;
865                               int                 rc = 0;
866                               regmatch_t          *tmp_data;
867 
868                               MATCHES_MEMSET( &tmp_matches );
869                               tmp_data = &tmp_matches.dn_data[0];
870 
871                               bv.bv_len = sizeof( buf ) - 1;
872                               bv.bv_val = buf;
873 
874                               /* Expand value regex */
875                               if ( a->acl_attrval_style == ACL_STYLE_REGEX )
876                                         tmp_matchesp = matches;
877                               else switch ( a->acl_dn_style ) {
878                               case ACL_STYLE_REGEX:
879                                         if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
880                                                   tmp_matchesp = matches;
881                                                   break;
882                                         }
883                               /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
884 
885                               case ACL_STYLE_BASE:
886                                         tmp_data[0].rm_so = 0;
887                                         tmp_data[0].rm_eo = e->e_nname.bv_len;
888                                         tmp_matches.dn_count = 1;
889                                         break;
890 
891                               case ACL_STYLE_ONE:
892                               case ACL_STYLE_SUBTREE:
893                               case ACL_STYLE_CHILDREN:
894                                         tmp_data[0].rm_so = 0;
895                                         tmp_data[0].rm_eo = e->e_nname.bv_len;
896                                         tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
897                                         tmp_data[1].rm_eo = e->e_nname.bv_len;
898                                         tmp_matches.dn_count = 2;
899                                         break;
900 
901                               default:
902                                         /* error */
903                                         rc = 1;
904                                         break;
905                               }
906 
907                               if ( rc ) {
908                                         return 1;
909                               }
910 
911                               if ( acl_string_expand( &bv, &bdn->a_pat,
912                                                             &e->e_nname,
913                                                             val, tmp_matchesp ) )
914                               {
915                                         return 1;
916                               }
917 
918                               if ( dnNormalize(0, NULL, NULL, &bv,
919                                                   &pat, op->o_tmpmemctx )
920                                                   != LDAP_SUCCESS )
921                               {
922                                         /* did not expand to a valid dn */
923                                         return 1;
924                               }
925 
926                     } else {
927                               pat = bdn->a_pat;
928                     }
929 
930                     patlen = pat.bv_len;
931                     odnlen = opndn->bv_len;
932                     if ( odnlen < patlen ) {
933                               goto dn_match_cleanup;
934 
935                     }
936 
937                     if ( bdn->a_style == ACL_STYLE_BASE ) {
938                               /* base dn -- entire object DN must match */
939                               if ( odnlen != patlen ) {
940                                         goto dn_match_cleanup;
941                               }
942 
943                     } else if ( bdn->a_style == ACL_STYLE_ONE ) {
944                               ber_len_t rdnlen = 0;
945 
946                               if ( odnlen <= patlen ) {
947                                         goto dn_match_cleanup;
948                               }
949 
950                               if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
951                                         goto dn_match_cleanup;
952                               }
953 
954                               rdnlen = dn_rdnlen( NULL, opndn );
955                               if ( rdnlen - ( odnlen - patlen - 1 ) != 0 ) {
956                                         goto dn_match_cleanup;
957                               }
958 
959                     } else if ( bdn->a_style == ACL_STYLE_SUBTREE ) {
960                               if ( odnlen > patlen && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
961                                         goto dn_match_cleanup;
962                               }
963 
964                     } else if ( bdn->a_style == ACL_STYLE_CHILDREN ) {
965                               if ( odnlen <= patlen ) {
966                                         goto dn_match_cleanup;
967                               }
968 
969                               if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
970                                         goto dn_match_cleanup;
971                               }
972 
973                     } else if ( bdn->a_style == ACL_STYLE_LEVEL ) {
974                               int                 level = bdn->a_level;
975                               struct berval       ndn;
976 
977                               if ( odnlen <= patlen ) {
978                                         goto dn_match_cleanup;
979                               }
980 
981                               if ( level > 0 && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) )
982                               {
983                                         goto dn_match_cleanup;
984                               }
985 
986                               ndn = *opndn;
987                               for ( ; level > 0; level-- ) {
988                                         if ( BER_BVISEMPTY( &ndn ) ) {
989                                                   goto dn_match_cleanup;
990                                         }
991                                         dnParent( &ndn, &ndn );
992                                         if ( ndn.bv_len < patlen ) {
993                                                   goto dn_match_cleanup;
994                                         }
995                               }
996 
997                               if ( ndn.bv_len != patlen ) {
998                                         goto dn_match_cleanup;
999                               }
1000                     }
1001 
1002                     got_match = !strcmp( pat.bv_val, &opndn->bv_val[ odnlen - patlen ] );
1003 
1004 dn_match_cleanup:;
1005                     if ( pat.bv_val != bdn->a_pat.bv_val ) {
1006                               slap_sl_free( pat.bv_val, op->o_tmpmemctx );
1007                     }
1008 
1009                     if ( !got_match ) {
1010                               return 1;
1011                     }
1012           }
1013 
1014           return 0;
1015 }
1016 
1017 static int
acl_mask_dnattr(Operation * op,Entry * e,struct berval * val,AccessControl * a,int count,AccessControlState * state,slap_mask_t * mask,slap_dn_access * bdn,struct berval * opndn)1018 acl_mask_dnattr(
1019           Operation           *op,
1020           Entry                         *e,
1021           struct berval                 *val,
1022           AccessControl                 *a,
1023           int                           count,
1024           AccessControlState  *state,
1025           slap_mask_t                             *mask,
1026           slap_dn_access                *bdn,
1027           struct berval                 *opndn )
1028 {
1029           Attribute *at;
1030           struct berval       bv;
1031           int                 rc, match = 0;
1032           const char          *text;
1033           const char          *attr = bdn->a_at->ad_cname.bv_val;
1034 
1035           assert( attr != NULL );
1036 
1037           if ( BER_BVISEMPTY( opndn ) ) {
1038                     return 1;
1039           }
1040 
1041           Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n", attr );
1042           bv = *opndn;
1043 
1044           /* see if asker is listed in dnattr */
1045           for ( at = attrs_find( e->e_attrs, bdn->a_at );
1046                     at != NULL;
1047                     at = attrs_find( at->a_next, bdn->a_at ) )
1048           {
1049                     if ( attr_valfind( at,
1050                               SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1051                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1052                               &bv, NULL, op->o_tmpmemctx ) == 0 )
1053                     {
1054                               /* found it */
1055                               match = 1;
1056                               break;
1057                     }
1058           }
1059 
1060           if ( match ) {
1061                     /* have a dnattr match. if this is a self clause then
1062                      * the target must also match the op dn.
1063                      */
1064                     if ( bdn->a_self ) {
1065                               /* check if the target is an attribute. */
1066                               if ( val == NULL ) return 1;
1067 
1068                               /* target is attribute, check if the attribute value
1069                                * is the op dn.
1070                                */
1071                               rc = value_match( &match, bdn->a_at,
1072                                         bdn->a_at->ad_type->sat_equality, 0,
1073                                         val, &bv, &text );
1074                               /* on match error or no match, fail the ACL clause */
1075                               if ( rc != LDAP_SUCCESS || match != 0 )
1076                                         return 1;
1077                     }
1078 
1079           } else {
1080                     /* no dnattr match, check if this is a self clause */
1081                     if ( ! bdn->a_self )
1082                               return 1;
1083 
1084                     /* this is a self clause, check if the target is an
1085                      * attribute.
1086                      */
1087                     if ( val == NULL )
1088                               return 1;
1089 
1090                     /* target is attribute, check if the attribute value
1091                      * is the op dn.
1092                      */
1093                     rc = value_match( &match, bdn->a_at,
1094                               bdn->a_at->ad_type->sat_equality, 0,
1095                               val, &bv, &text );
1096 
1097                     /* on match error or no match, fail the ACL clause */
1098                     if ( rc != LDAP_SUCCESS || match != 0 )
1099                               return 1;
1100           }
1101 
1102           return 0;
1103 }
1104 
1105 
1106 /*
1107  * slap_acl_mask - modifies mask based upon the given acl and the
1108  * requested access to entry e, attribute attr, value val.  if val
1109  * is null, access to the whole attribute is assumed (all values).
1110  *
1111  * returns          0         access NOT allowed
1112  *                  1         access allowed
1113  */
1114 
1115 static slap_control_t
slap_acl_mask(AccessControl * a,AccessControl * prev,slap_mask_t * mask,Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,AclRegexMatches * matches,int count,AccessControlState * state,slap_access_t access)1116 slap_acl_mask(
1117           AccessControl                 *a,
1118           AccessControl                 *prev,
1119           slap_mask_t                   *mask,
1120           Operation           *op,
1121           Entry                         *e,
1122           AttributeDescription          *desc,
1123           struct berval                 *val,
1124           AclRegexMatches               *matches,
1125           int                           count,
1126           AccessControlState  *state,
1127           slap_access_t       access )
1128 {
1129           int                 i;
1130           Access              *b;
1131 #ifdef LDAP_DEBUG
1132           char                accessmaskbuf[ACCESSMASK_MAXLEN];
1133 #endif /* DEBUG */
1134           const char          *attr;
1135 #ifdef SLAP_DYNACL
1136           slap_mask_t         a2pmask = ACL_ACCESS2PRIV( access );
1137 #endif /* SLAP_DYNACL */
1138 
1139           assert( a != NULL );
1140           assert( mask != NULL );
1141           assert( desc != NULL );
1142 
1143           attr = desc->ad_cname.bv_val;
1144 
1145           assert( attr != NULL );
1146 
1147           Debug( LDAP_DEBUG_ACL,
1148                     "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
1149                     e->e_dn, attr );
1150 
1151           Debug( LDAP_DEBUG_ACL,
1152                     "=> acl_mask: to %s by \"%s\", (%s) \n",
1153                     val ? "value" : "all values",
1154                     op->o_ndn.bv_val ?  op->o_ndn.bv_val : "",
1155                     accessmask2str( *mask, accessmaskbuf, 1 ) );
1156 
1157 
1158           b = a->acl_access;
1159           i = 1;
1160 
1161           for ( ; b != NULL; b = b->a_next, i++ ) {
1162                     slap_mask_t oldmask, modmask;
1163 
1164                     ACL_INVALIDATE( modmask );
1165 
1166                     /* check for the "self" modifier in the <access> field */
1167                     if ( b->a_dn.a_self ) {
1168                               const char *dummy;
1169                               int rc, match = 0;
1170 
1171                               ACL_RECORD_VALUE_STATE;
1172 
1173                               /* must have DN syntax */
1174                               if ( desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName &&
1175                                         !is_at_syntax( desc->ad_type, SLAPD_NAMEUID_SYNTAX )) continue;
1176 
1177                               /* check if the target is an attribute. */
1178                               if ( val == NULL ) continue;
1179 
1180                               /* a DN must be present */
1181                               if ( BER_BVISEMPTY( &op->o_ndn ) ) {
1182                                         continue;
1183                               }
1184 
1185                               /* target is attribute, check if the attribute value
1186                                * is the op dn.
1187                                */
1188                               rc = value_match( &match, desc,
1189                                         desc->ad_type->sat_equality, 0,
1190                                         val, &op->o_ndn, &dummy );
1191                               /* on match error or no match, fail the ACL clause */
1192                               if ( rc != LDAP_SUCCESS || match != 0 )
1193                                         continue;
1194                     }
1195 
1196                     /* AND <who> clauses */
1197                     if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
1198                               Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
1199                                         b->a_dn_pat.bv_val );
1200                               /*
1201                                * if access applies to the entry itself, and the
1202                                * user is bound as somebody in the same namespace as
1203                                * the entry, OR the given dn matches the dn pattern
1204                                */
1205                               /*
1206                                * NOTE: styles "anonymous", "users" and "self"
1207                                * have been moved to enum slap_style_t, whose
1208                                * value is set in a_dn_style; however, the string
1209                                * is maintained in a_dn_pat.
1210                                */
1211 
1212                               if ( acl_mask_dn( op, e, val, a, matches,
1213                                         &b->a_dn, &op->o_ndn ) )
1214                               {
1215                                         continue;
1216                               }
1217                     }
1218 
1219                     if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
1220                               struct berval       ndn;
1221 
1222                               Debug( LDAP_DEBUG_ACL, "<= check a_realdn_pat: %s\n",
1223                                         b->a_realdn_pat.bv_val );
1224                               /*
1225                                * if access applies to the entry itself, and the
1226                                * user is bound as somebody in the same namespace as
1227                                * the entry, OR the given dn matches the dn pattern
1228                                */
1229                               /*
1230                                * NOTE: styles "anonymous", "users" and "self"
1231                                * have been moved to enum slap_style_t, whose
1232                                * value is set in a_dn_style; however, the string
1233                                * is maintained in a_dn_pat.
1234                                */
1235 
1236                               if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1237                               {
1238                                         ndn = op->o_conn->c_ndn;
1239                               } else {
1240                                         ndn = op->o_ndn;
1241                               }
1242 
1243                               if ( acl_mask_dn( op, e, val, a, matches,
1244                                         &b->a_realdn, &ndn ) )
1245                               {
1246                                         continue;
1247                               }
1248                     }
1249 
1250                     if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1251                               if ( ! op->o_conn->c_listener ) {
1252                                         continue;
1253                               }
1254                               Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
1255                                         b->a_sockurl_pat.bv_val );
1256 
1257                               if ( !ber_bvccmp( &b->a_sockurl_pat, '*' ) ) {
1258                                         if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
1259                                                   if ( !regex_matches( &b->a_sockurl_pat, op->o_conn->c_listener_url.bv_val,
1260                                                                       &e->e_nname, val, matches ) )
1261                                                   {
1262                                                             continue;
1263                                                   }
1264 
1265                                         } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) {
1266                                                   struct berval       bv;
1267                                                   char buf[ACL_BUF_SIZE];
1268 
1269                                                   bv.bv_len = sizeof( buf ) - 1;
1270                                                   bv.bv_val = buf;
1271                                                   if ( acl_string_expand( &bv, &b->a_sockurl_pat, &e->e_nname, val, matches ) )
1272                                                   {
1273                                                             continue;
1274                                                   }
1275 
1276                                                   if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 )
1277                                                   {
1278                                                             continue;
1279                                                   }
1280 
1281                                         } else {
1282                                                   if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 )
1283                                                   {
1284                                                             continue;
1285                                                   }
1286                                         }
1287                               }
1288                     }
1289 
1290                     if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1291                               if ( !op->o_conn->c_peer_domain.bv_val ) {
1292                                         continue;
1293                               }
1294                               Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
1295                                         b->a_domain_pat.bv_val );
1296                               if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) {
1297                                         if ( b->a_domain_style == ACL_STYLE_REGEX) {
1298                                                   if ( !regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val,
1299                                                                       &e->e_nname, val, matches ) )
1300                                                   {
1301                                                             continue;
1302                                                   }
1303                                         } else {
1304                                                   char buf[ACL_BUF_SIZE];
1305 
1306                                                   struct berval       cmp = op->o_conn->c_peer_domain;
1307                                                   struct berval       pat = b->a_domain_pat;
1308 
1309                                                   if ( b->a_domain_expand ) {
1310                                                             struct berval bv;
1311 
1312                                                             bv.bv_len = sizeof(buf) - 1;
1313                                                             bv.bv_val = buf;
1314 
1315                                                             if ( acl_string_expand(&bv, &b->a_domain_pat, &e->e_nname, val, matches) )
1316                                                             {
1317                                                                       continue;
1318                                                             }
1319                                                             pat = bv;
1320                                                   }
1321 
1322                                                   if ( b->a_domain_style == ACL_STYLE_SUBTREE ) {
1323                                                             int offset = cmp.bv_len - pat.bv_len;
1324                                                             if ( offset < 0 ) {
1325                                                                       continue;
1326                                                             }
1327 
1328                                                             if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) {
1329                                                                       continue;
1330                                                             }
1331 
1332                                                             /* trim the domain */
1333                                                             cmp.bv_val = &cmp.bv_val[ offset ];
1334                                                             cmp.bv_len -= offset;
1335                                                   }
1336 
1337                                                   if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) {
1338                                                             continue;
1339                                                   }
1340                                         }
1341                               }
1342                     }
1343 
1344                     if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1345                               if ( !op->o_conn->c_peer_name.bv_val ) {
1346                                         continue;
1347                               }
1348                               Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
1349                                         b->a_peername_pat.bv_val );
1350                               if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) {
1351                                         if ( b->a_peername_style == ACL_STYLE_REGEX ) {
1352                                                   if ( !regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val,
1353                                                                       &e->e_nname, val, matches ) )
1354                                                   {
1355                                                             continue;
1356                                                   }
1357 
1358                                         } else {
1359                                                   /* try exact match */
1360                                                   if ( b->a_peername_style == ACL_STYLE_BASE ) {
1361                                                             if ( ber_bvstrcasecmp( &b->a_peername_pat, &op->o_conn->c_peer_name ) != 0 ) {
1362                                                                       continue;
1363                                                             }
1364 
1365                                                   } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) {
1366                                                             struct berval       bv;
1367                                                             char buf[ACL_BUF_SIZE];
1368 
1369                                                             bv.bv_len = sizeof( buf ) - 1;
1370                                                             bv.bv_val = buf;
1371                                                             if ( acl_string_expand( &bv, &b->a_peername_pat, &e->e_nname, val, matches ) )
1372                                                             {
1373                                                                       continue;
1374                                                             }
1375 
1376                                                             if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) {
1377                                                                       continue;
1378                                                             }
1379 
1380                                                   /* extract IP and try exact match */
1381                                                   } else if ( b->a_peername_style == ACL_STYLE_IP ) {
1382                                                             char                *port;
1383                                                             char                buf[STRLENOF("255.255.255.255") + 1];
1384                                                             struct berval       ip;
1385                                                             unsigned long       addr;
1386                                                             int                 port_number = -1;
1387 
1388                                                             if ( strncasecmp( op->o_conn->c_peer_name.bv_val,
1389                                                                                           acl_bv_ip_eq.bv_val,
1390                                                                                           acl_bv_ip_eq.bv_len ) != 0 )
1391                                                                       continue;
1392 
1393                                                             ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ip_eq.bv_len;
1394                                                             ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ip_eq.bv_len;
1395 
1396                                                             port = strrchr( ip.bv_val, ':' );
1397                                                             if ( port ) {
1398                                                                       ip.bv_len = port - ip.bv_val;
1399                                                                       ++port;
1400                                                                       if ( lutil_atoi( &port_number, port ) != 0 )
1401                                                                                 continue;
1402                                                             }
1403 
1404                                                             /* the port check can be anticipated here */
1405                                                             if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
1406                                                                       continue;
1407 
1408                                                             /* address longer than expected? */
1409                                                             if ( ip.bv_len >= sizeof(buf) )
1410                                                                       continue;
1411 
1412                                                             AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
1413                                                             buf[ ip.bv_len ] = '\0';
1414 
1415                                                             addr = inet_addr( buf );
1416 
1417                                                             /* unable to convert? */
1418                                                             if ( addr == (unsigned long)(-1) )
1419                                                                       continue;
1420 
1421                                                             if ( (addr & b->a_peername_mask) != b->a_peername_addr )
1422                                                                       continue;
1423 
1424 #ifdef LDAP_PF_INET6
1425                                                   /* extract IPv6 and try exact match */
1426                                                   } else if ( b->a_peername_style == ACL_STYLE_IPV6 ) {
1427                                                             char                *port;
1428                                                             char                buf[STRLENOF("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF") + 1];
1429                                                             struct berval       ip;
1430                                                             struct in6_addr     addr;
1431                                                             int                 port_number = -1;
1432 
1433                                                             if ( strncasecmp( op->o_conn->c_peer_name.bv_val,
1434                                                                                           acl_bv_ipv6_eq.bv_val,
1435                                                                                           acl_bv_ipv6_eq.bv_len ) != 0 )
1436                                                                       continue;
1437 
1438                                                             ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ipv6_eq.bv_len;
1439                                                             ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ipv6_eq.bv_len;
1440 
1441                                                             port = strrchr( ip.bv_val, ']' );
1442                                                             if ( port ) {
1443                                                                       ip.bv_len = port - ip.bv_val;
1444                                                                       ++port;
1445                                                                       if ( port[0] == ':' && lutil_atoi( &port_number, ++port ) != 0 )
1446                                                                                 continue;
1447                                                             }
1448 
1449                                                             /* the port check can be anticipated here */
1450                                                             if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
1451                                                                       continue;
1452 
1453                                                             /* address longer than expected? */
1454                                                             if ( ip.bv_len >= sizeof(buf) )
1455                                                                       continue;
1456 
1457                                                             AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
1458                                                             buf[ ip.bv_len ] = '\0';
1459 
1460                                                             if ( inet_pton( AF_INET6, buf, &addr ) != 1 )
1461                                                                       continue;
1462 
1463                                                             /* check mask */
1464                                                             if ( !slap_addr6_mask( &addr, &b->a_peername_mask6, &b->a_peername_addr6 ) )
1465                                                                       continue;
1466 #endif /* LDAP_PF_INET6 */
1467 
1468 #ifdef LDAP_PF_LOCAL
1469                                                   /* extract path and try exact match */
1470                                                   } else if ( b->a_peername_style == ACL_STYLE_PATH ) {
1471                                                             struct berval path;
1472 
1473                                                             if ( strncmp( op->o_conn->c_peer_name.bv_val,
1474                                                                                           acl_bv_path_eq.bv_val,
1475                                                                                           acl_bv_path_eq.bv_len ) != 0 )
1476                                                                       continue;
1477 
1478                                                             path.bv_val = op->o_conn->c_peer_name.bv_val
1479                                                                       + acl_bv_path_eq.bv_len;
1480                                                             path.bv_len = op->o_conn->c_peer_name.bv_len
1481                                                                       - acl_bv_path_eq.bv_len;
1482 
1483                                                             if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
1484                                                                       continue;
1485 
1486 #endif /* LDAP_PF_LOCAL */
1487 
1488                                                   /* exact match (very unlikely...) */
1489                                                   } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) {
1490                                                                       continue;
1491                                                   }
1492                                         }
1493                               }
1494                     }
1495 
1496                     if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
1497                               if ( BER_BVISNULL( &op->o_conn->c_sock_name ) ) {
1498                                         continue;
1499                               }
1500                               Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
1501                                         b->a_sockname_pat.bv_val );
1502                               if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) {
1503                                         if ( b->a_sockname_style == ACL_STYLE_REGEX) {
1504                                                   if ( !regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val,
1505                                                                       &e->e_nname, val, matches ) )
1506                                                   {
1507                                                             continue;
1508                                                   }
1509 
1510                                         } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) {
1511                                                   struct berval       bv;
1512                                                   char buf[ACL_BUF_SIZE];
1513 
1514                                                   bv.bv_len = sizeof( buf ) - 1;
1515                                                   bv.bv_val = buf;
1516                                                   if ( acl_string_expand( &bv, &b->a_sockname_pat, &e->e_nname, val, matches ) )
1517                                                   {
1518                                                             continue;
1519                                                   }
1520 
1521                                                   if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) {
1522                                                             continue;
1523                                                   }
1524 
1525                                         } else {
1526                                                   if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 ) {
1527                                                             continue;
1528                                                   }
1529                                         }
1530                               }
1531                     }
1532 
1533                     if ( b->a_dn_at != NULL ) {
1534                               if ( acl_mask_dnattr( op, e, val, a,
1535                                                   count, state, mask,
1536                                                   &b->a_dn, &op->o_ndn ) )
1537                               {
1538                                         continue;
1539                               }
1540                     }
1541 
1542                     if ( b->a_realdn_at != NULL ) {
1543                               struct berval       ndn;
1544 
1545                               if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1546                               {
1547                                         ndn = op->o_conn->c_ndn;
1548                               } else {
1549                                         ndn = op->o_ndn;
1550                               }
1551 
1552                               if ( acl_mask_dnattr( op, e, val, a,
1553                                                   count, state, mask,
1554                                                   &b->a_realdn, &ndn ) )
1555                               {
1556                                         continue;
1557                               }
1558                     }
1559 
1560                     if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
1561                               struct berval bv;
1562                               struct berval ndn = BER_BVNULL;
1563                               int rc;
1564 
1565                               if ( op->o_ndn.bv_len == 0 ) {
1566                                         continue;
1567                               }
1568 
1569                               Debug( LDAP_DEBUG_ACL, "<= check a_group_pat: %s\n",
1570                                         b->a_group_pat.bv_val );
1571 
1572                               /* b->a_group is an unexpanded entry name, expanded it should be an
1573                                * entry with objectclass group* and we test to see if odn is one of
1574                                * the values in the attribute group
1575                                */
1576                               /* see if asker is listed in dnattr */
1577                               if ( b->a_group_style == ACL_STYLE_EXPAND ) {
1578                                         char                buf[ACL_BUF_SIZE];
1579                                         AclRegexMatches     tmp_matches,
1580                                                             *tmp_matchesp = &tmp_matches;
1581                                         regmatch_t          *tmp_data;
1582 
1583                                         MATCHES_MEMSET( &tmp_matches );
1584                                         tmp_data = &tmp_matches.dn_data[0];
1585 
1586                                         bv.bv_len = sizeof(buf) - 1;
1587                                         bv.bv_val = buf;
1588 
1589                                         rc = 0;
1590 
1591                                         if ( a->acl_attrval_style == ACL_STYLE_REGEX )
1592                                                   tmp_matchesp = matches;
1593                                         else switch ( a->acl_dn_style ) {
1594                                         case ACL_STYLE_REGEX:
1595                                                   if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1596                                                             tmp_matchesp = matches;
1597                                                             break;
1598                                                   }
1599 
1600                                         /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1601                                         case ACL_STYLE_BASE:
1602                                                   tmp_data[0].rm_so = 0;
1603                                                   tmp_data[0].rm_eo = e->e_nname.bv_len;
1604                                                   tmp_matches.dn_count = 1;
1605                                                   break;
1606 
1607                                         case ACL_STYLE_ONE:
1608                                         case ACL_STYLE_SUBTREE:
1609                                         case ACL_STYLE_CHILDREN:
1610                                                   tmp_data[0].rm_so = 0;
1611                                                   tmp_data[0].rm_eo = e->e_nname.bv_len;
1612 
1613                                                   tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1614                                                   tmp_data[1].rm_eo = e->e_nname.bv_len;
1615                                                   tmp_matches.dn_count = 2;
1616                                                   break;
1617 
1618                                         default:
1619                                                   /* error */
1620                                                   rc = 1;
1621                                                   break;
1622                                         }
1623 
1624                                         if ( rc ) {
1625                                                   continue;
1626                                         }
1627 
1628                                         if ( acl_string_expand( &bv, &b->a_group_pat,
1629                                                             &e->e_nname, val,
1630                                                             tmp_matchesp ) )
1631                                         {
1632                                                   continue;
1633                                         }
1634 
1635                                         if ( dnNormalize( 0, NULL, NULL, &bv, &ndn,
1636                                                             op->o_tmpmemctx ) != LDAP_SUCCESS )
1637                                         {
1638                                                   /* did not expand to a valid dn */
1639                                                   continue;
1640                                         }
1641 
1642                                         bv = ndn;
1643 
1644                               } else {
1645                                         bv = b->a_group_pat;
1646                               }
1647 
1648                               rc = backend_group( op, e, &bv, &op->o_ndn,
1649                                         b->a_group_oc, b->a_group_at );
1650 
1651                               if ( ndn.bv_val ) {
1652                                         slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
1653                               }
1654 
1655                               if ( rc != 0 ) {
1656                                         continue;
1657                               }
1658                     }
1659 
1660                     if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1661                               struct berval       bv;
1662                               char                buf[ACL_BUF_SIZE];
1663 
1664                               Debug( LDAP_DEBUG_ACL, "<= check a_set_pat: %s\n",
1665                                         b->a_set_pat.bv_val );
1666 
1667                               if ( b->a_set_style == ACL_STYLE_EXPAND ) {
1668                                         AclRegexMatches     tmp_matches,
1669                                                             *tmp_matchesp = &tmp_matches;
1670                                         int                 rc = 0;
1671                                         regmatch_t          *tmp_data;
1672 
1673                                         MATCHES_MEMSET( &tmp_matches );
1674                                         tmp_data = &tmp_matches.dn_data[0];
1675 
1676                                         bv.bv_len = sizeof( buf ) - 1;
1677                                         bv.bv_val = buf;
1678 
1679                                         rc = 0;
1680 
1681                                         if ( a->acl_attrval_style == ACL_STYLE_REGEX )
1682                                                   tmp_matchesp = matches;
1683                                         else switch ( a->acl_dn_style ) {
1684                                         case ACL_STYLE_REGEX:
1685                                                   if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1686                                                             tmp_matchesp = matches;
1687                                                             break;
1688                                                   }
1689 
1690                                         /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1691                                         case ACL_STYLE_BASE:
1692                                                   tmp_data[0].rm_so = 0;
1693                                                   tmp_data[0].rm_eo = e->e_nname.bv_len;
1694                                                   tmp_matches.dn_count = 1;
1695                                                   break;
1696 
1697                                         case ACL_STYLE_ONE:
1698                                         case ACL_STYLE_SUBTREE:
1699                                         case ACL_STYLE_CHILDREN:
1700                                                   tmp_data[0].rm_so = 0;
1701                                                   tmp_data[0].rm_eo = e->e_nname.bv_len;
1702                                                   tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1703                                                   tmp_data[1].rm_eo = e->e_nname.bv_len; tmp_matches.dn_count = 2;
1704                                                   break;
1705 
1706                                         default:
1707                                                   /* error */
1708                                                   rc = 1;
1709                                                   break;
1710                                         }
1711 
1712                                         if ( rc ) {
1713                                                   continue;
1714                                         }
1715 
1716                                         if ( acl_string_expand( &bv, &b->a_set_pat,
1717                                                             &e->e_nname, val,
1718                                                             tmp_matchesp ) )
1719                                         {
1720                                                   continue;
1721                                         }
1722 
1723                               } else {
1724                                         bv = b->a_set_pat;
1725                               }
1726 
1727                               if ( acl_match_set( &bv, op, e, NULL ) == 0 ) {
1728                                         continue;
1729                               }
1730                     }
1731 
1732                     if ( b->a_authz.sai_ssf ) {
1733                               Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
1734                                         b->a_authz.sai_ssf, op->o_ssf );
1735                               if ( b->a_authz.sai_ssf >  op->o_ssf ) {
1736                                         continue;
1737                               }
1738                     }
1739 
1740                     if ( b->a_authz.sai_transport_ssf ) {
1741                               Debug( LDAP_DEBUG_ACL,
1742                                         "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
1743                                         b->a_authz.sai_transport_ssf, op->o_transport_ssf );
1744                               if ( b->a_authz.sai_transport_ssf >  op->o_transport_ssf ) {
1745                                         continue;
1746                               }
1747                     }
1748 
1749                     if ( b->a_authz.sai_tls_ssf ) {
1750                               Debug( LDAP_DEBUG_ACL,
1751                                         "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
1752                                         b->a_authz.sai_tls_ssf, op->o_tls_ssf );
1753                               if ( b->a_authz.sai_tls_ssf >  op->o_tls_ssf ) {
1754                                         continue;
1755                               }
1756                     }
1757 
1758                     if ( b->a_authz.sai_sasl_ssf ) {
1759                               Debug( LDAP_DEBUG_ACL,
1760                                         "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
1761                                         b->a_authz.sai_sasl_ssf, op->o_sasl_ssf );
1762                               if ( b->a_authz.sai_sasl_ssf >          op->o_sasl_ssf ) {
1763                                         continue;
1764                               }
1765                     }
1766 
1767 #ifdef SLAP_DYNACL
1768                     if ( b->a_dynacl ) {
1769                               slap_dynacl_t       *da;
1770                               slap_access_t       tgrant, tdeny;
1771 
1772                               Debug( LDAP_DEBUG_ACL, "<= check a_dynacl\n" );
1773 
1774                               /* this case works different from the others above.
1775                                * since dynamic ACL's themselves give permissions, we need
1776                                * to first check b->a_access_mask, the ACL's access level.
1777                                */
1778                               /* first check if the right being requested
1779                                * is allowed by the ACL clause.
1780                                */
1781                               if ( ! ACL_PRIV_ISSET( b->a_access_mask, a2pmask ) ) {
1782                                         continue;
1783                               }
1784 
1785                               /* start out with nothing granted, nothing denied */
1786                               ACL_INVALIDATE(tgrant);
1787                               ACL_INVALIDATE(tdeny);
1788 
1789                               for ( da = b->a_dynacl; da; da = da->da_next ) {
1790                                         slap_access_t       grant,
1791                                                             deny;
1792 
1793                                         ACL_INVALIDATE(grant);
1794                                         ACL_INVALIDATE(deny);
1795 
1796                                         Debug( LDAP_DEBUG_ACL, "    <= check a_dynacl: %s\n",
1797                                                   da->da_name );
1798 
1799                                         /*
1800                                          * XXXmanu Only DN matches are supplied
1801                                          * sending attribute values matches require
1802                                          * an API update
1803                                          */
1804                                         (void)da->da_mask( da->da_private, op, e, desc,
1805                                                   val, matches->dn_count, matches->dn_data,
1806                                                   &grant, &deny );
1807 
1808                                         tgrant |= grant;
1809                                         tdeny |= deny;
1810                               }
1811 
1812                               /* remove anything that the ACL clause does not allow */
1813                               tgrant &= b->a_access_mask & ACL_PRIV_MASK;
1814                               tdeny &= ACL_PRIV_MASK;
1815 
1816                               /* see if we have anything to contribute */
1817                               if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) {
1818                                         continue;
1819                               }
1820 
1821                               /* this could be improved by changing slap_acl_mask so that it can deal with
1822                                * by clauses that return grant/deny pairs.  Right now, it does either
1823                                * additive or subtractive rights, but not both at the same time.  So,
1824                                * we need to combine the grant/deny pair into a single rights mask in
1825                                * a smart way:      if either grant or deny is "empty", then we use the
1826                                * opposite as is, otherwise we remove any denied rights from the grant
1827                                * rights mask and construct an additive mask.
1828                                */
1829                               if (ACL_IS_INVALID(tdeny)) {
1830                                         modmask = tgrant | ACL_PRIV_ADDITIVE;
1831 
1832                               } else if (ACL_IS_INVALID(tgrant)) {
1833                                         modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
1834 
1835                               } else {
1836                                         modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
1837                               }
1838 
1839                     } else
1840 #endif /* SLAP_DYNACL */
1841                     {
1842                               modmask = b->a_access_mask;
1843                     }
1844 
1845                     Debug( LDAP_DEBUG_ACL,
1846                               "<= acl_mask: [%d] applying %s (%s)\n",
1847                               i, accessmask2str( modmask, accessmaskbuf, 1 ),
1848                               b->a_type == ACL_CONTINUE
1849                                         ? "continue"
1850                                         : b->a_type == ACL_BREAK
1851                                                   ? "break"
1852                                                   : "stop" );
1853                     /* save old mask */
1854                     oldmask = *mask;
1855 
1856                     if( ACL_IS_ADDITIVE(modmask) ) {
1857                               /* add privs */
1858                               ACL_PRIV_SET( *mask, modmask );
1859 
1860                               /* cleanup */
1861                               ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1862 
1863                     } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
1864                               /* subtract privs */
1865                               ACL_PRIV_CLR( *mask, modmask );
1866 
1867                               /* cleanup */
1868                               ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1869 
1870                     } else {
1871                               /* assign privs */
1872                               *mask = modmask;
1873                     }
1874 
1875                     Debug( LDAP_DEBUG_ACL,
1876                               "<= acl_mask: [%d] mask: %s\n",
1877                               i, accessmask2str(*mask, accessmaskbuf, 1) );
1878 
1879                     if( b->a_type == ACL_CONTINUE ) {
1880                               continue;
1881 
1882                     } else if ( b->a_type == ACL_BREAK ) {
1883                               return ACL_BREAK;
1884 
1885                     } else {
1886                               return ACL_STOP;
1887                     }
1888           }
1889 
1890           /* implicit "by * none" clause */
1891           ACL_INIT(*mask);
1892 
1893           Debug( LDAP_DEBUG_ACL,
1894                     "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
1895                     accessmask2str(*mask, accessmaskbuf, 1) );
1896           return ACL_STOP;
1897 }
1898 
1899 /*
1900  * acl_check_modlist - check access control on the given entry to see if
1901  * it allows the given modifications by the user associated with op.
1902  * returns          1         if mods allowed ok
1903  *                  0         mods not allowed
1904  */
1905 
1906 int
acl_check_modlist(Operation * op,Entry * e,Modifications * mlist)1907 acl_check_modlist(
1908           Operation *op,
1909           Entry     *e,
1910           Modifications       *mlist )
1911 {
1912           struct berval *bv;
1913           AccessControlState state = ACL_STATE_INIT;
1914           Backend *be;
1915           int be_null = 0;
1916           int ret = 1; /* default is access allowed */
1917 
1918           be = op->o_bd;
1919           if ( be == NULL ) {
1920                     be = LDAP_STAILQ_FIRST(&backendDB);
1921                     be_null = 1;
1922                     op->o_bd = be;
1923           }
1924           assert( be != NULL );
1925 
1926           /* If ADD attribute checking is not enabled, just allow it */
1927           if ( op->o_tag == LDAP_REQ_ADD && !SLAP_DBACL_ADD( be ))
1928                     return 1;
1929 
1930           /* short circuit root database access */
1931           if ( be_isroot( op ) ) {
1932                     Debug( LDAP_DEBUG_ACL,
1933                               "<= acl_access_allowed: granted to database root\n" );
1934                     goto done;
1935           }
1936 
1937           /* use backend default access if no backend acls */
1938           if( op->o_bd != NULL && op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
1939                     Debug( LDAP_DEBUG_ACL,
1940                               "=> access_allowed: backend default %s access %s to \"%s\"\n",
1941                               access2str( ACL_WRITE ),
1942                               op->o_bd->be_dfltaccess >= ACL_WRITE
1943                                         ? "granted" : "denied",
1944                               op->o_dn.bv_val );
1945                     ret = (op->o_bd->be_dfltaccess >= ACL_WRITE);
1946                     goto done;
1947           }
1948 
1949           for ( ; mlist != NULL; mlist = mlist->sml_next ) {
1950                     /*
1951                      * Internal mods are ignored by ACL_WRITE checking
1952                      */
1953                     if ( mlist->sml_flags & SLAP_MOD_INTERNAL ) {
1954                               Debug( LDAP_DEBUG_ACL, "acl: internal mod %s:"
1955                                         " modify access granted\n",
1956                                         mlist->sml_desc->ad_cname.bv_val );
1957                               continue;
1958                     }
1959 
1960                     /*
1961                      * no-user-modification operational attributes are ignored
1962                      * by ACL_WRITE checking as any found here are not provided
1963                      * by the user
1964                      */
1965                     if ( is_at_no_user_mod( mlist->sml_desc->ad_type )
1966                                         && ! ( mlist->sml_flags & SLAP_MOD_MANAGING ) )
1967                     {
1968                               Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
1969                                         " modify access granted\n",
1970                                         mlist->sml_desc->ad_cname.bv_val );
1971                               continue;
1972                     }
1973 
1974                     switch ( mlist->sml_op ) {
1975                     case LDAP_MOD_REPLACE:
1976                     case LDAP_MOD_INCREMENT:
1977                               /*
1978                                * We must check both permission to delete the whole
1979                                * attribute and permission to add the specific attributes.
1980                                * This prevents abuse from selfwriters.
1981                                */
1982                               if ( ! access_allowed( op, e,
1983                                         mlist->sml_desc, NULL,
1984                                         ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
1985                                         &state ) )
1986                               {
1987                                         ret = 0;
1988                                         goto done;
1989                               }
1990 
1991                               if ( mlist->sml_values == NULL ) break;
1992 
1993                               /* fall thru to check value to add */
1994 
1995                     case LDAP_MOD_ADD:
1996                     case SLAP_MOD_ADD_IF_NOT_PRESENT:
1997                               assert( mlist->sml_values != NULL );
1998 
1999                               if ( mlist->sml_op == SLAP_MOD_ADD_IF_NOT_PRESENT
2000                                         && attr_find( e->e_attrs, mlist->sml_desc ) )
2001                               {
2002                                         break;
2003                               }
2004 
2005                               for ( bv = mlist->sml_nvalues
2006                                                   ? mlist->sml_nvalues : mlist->sml_values;
2007                                         bv->bv_val != NULL; bv++ )
2008                               {
2009                                         if ( ! access_allowed( op, e,
2010                                                   mlist->sml_desc, bv,
2011                                                   ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WADD,
2012                                                   &state ) )
2013                                         {
2014                                                   ret = 0;
2015                                                   goto done;
2016                                         }
2017                               }
2018                               break;
2019 
2020                     case LDAP_MOD_DELETE:
2021                     case SLAP_MOD_SOFTDEL:
2022                               if ( mlist->sml_values == NULL ) {
2023                                         if ( ! access_allowed( op, e,
2024                                                   mlist->sml_desc, NULL,
2025                                                   ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2026                                                   &state ) )
2027                                         {
2028                                                   ret = 0;
2029                                                   goto done;
2030                                         }
2031                                         break;
2032                               }
2033                               for ( bv = mlist->sml_nvalues
2034                                                   ? mlist->sml_nvalues : mlist->sml_values;
2035                                         bv->bv_val != NULL; bv++ )
2036                               {
2037                                         if ( ! access_allowed( op, e,
2038                                                   mlist->sml_desc, bv,
2039                                                   ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2040                                                   &state ) )
2041                                         {
2042                                                   ret = 0;
2043                                                   goto done;
2044                                         }
2045                               }
2046                               break;
2047 
2048                     case SLAP_MOD_SOFTADD:
2049                               /* allow adding attribute via modrdn thru */
2050                               break;
2051 
2052                     default:
2053                               assert( 0 );
2054                               /* not reached */
2055                               ret = 0;
2056                               break;
2057                     }
2058           }
2059 
2060 done:
2061           if (be_null) op->o_bd = NULL;
2062           return( ret );
2063 }
2064 
2065 int
acl_get_part(struct berval * list,int ix,char sep,struct berval * bv)2066 acl_get_part(
2067           struct berval       *list,
2068           int                 ix,
2069           char                sep,
2070           struct berval       *bv )
2071 {
2072           int       len;
2073           char      *p;
2074 
2075           if ( bv ) {
2076                     BER_BVZERO( bv );
2077           }
2078           len = list->bv_len;
2079           p = list->bv_val;
2080           while ( len >= 0 && --ix >= 0 ) {
2081                     while ( --len >= 0 && *p++ != sep )
2082                               ;
2083           }
2084           while ( len >= 0 && *p == ' ' ) {
2085                     len--;
2086                     p++;
2087           }
2088           if ( len < 0 ) {
2089                     return -1;
2090           }
2091 
2092           if ( !bv ) {
2093                     return 0;
2094           }
2095 
2096           bv->bv_val = p;
2097           while ( --len >= 0 && *p != sep ) {
2098                     bv->bv_len++;
2099                     p++;
2100           }
2101           while ( bv->bv_len > 0 && *--p == ' ' ) {
2102                     bv->bv_len--;
2103           }
2104 
2105           return bv->bv_len;
2106 }
2107 
2108 typedef struct acl_set_gather_t {
2109           SetCookie           *cookie;
2110           BerVarray           bvals;
2111 } acl_set_gather_t;
2112 
2113 static int
acl_set_cb_gather(Operation * op,SlapReply * rs)2114 acl_set_cb_gather( Operation *op, SlapReply *rs )
2115 {
2116           acl_set_gather_t    *p = (acl_set_gather_t *)op->o_callback->sc_private;
2117 
2118           if ( rs->sr_type == REP_SEARCH ) {
2119                     BerValue  bvals[ 2 ];
2120                     BerVarray bvalsp = NULL;
2121                     int                 j;
2122 
2123                     for ( j = 0; !BER_BVISNULL( &rs->sr_attrs[ j ].an_name ); j++ ) {
2124                               AttributeDescription          *desc = rs->sr_attrs[ j ].an_desc;
2125 
2126                               if ( desc == NULL ) {
2127                                         continue;
2128                               }
2129 
2130                               if ( desc == slap_schema.si_ad_entryDN ) {
2131                                         bvalsp = bvals;
2132                                         bvals[ 0 ] = rs->sr_entry->e_nname;
2133                                         BER_BVZERO( &bvals[ 1 ] );
2134 
2135                               } else {
2136                                         Attribute *a;
2137 
2138                                         a = attr_find( rs->sr_entry->e_attrs, desc );
2139                                         if ( a != NULL ) {
2140                                                   bvalsp = a->a_nvals;
2141                                         }
2142                               }
2143 
2144                               if ( bvalsp ) {
2145                                         p->bvals = slap_set_join( p->cookie, p->bvals,
2146                                                             ( '|' | SLAP_SET_RREF ), bvalsp );
2147                               }
2148                     }
2149 
2150           } else {
2151                     switch ( rs->sr_type ) {
2152                     case REP_SEARCHREF:
2153                     case REP_INTERMEDIATE:
2154                               /* ignore */
2155                               break;
2156 
2157                     default:
2158                               assert( rs->sr_type == REP_RESULT );
2159                               break;
2160                     }
2161           }
2162 
2163           return 0;
2164 }
2165 
2166 BerVarray
acl_set_gather(SetCookie * cookie,struct berval * name,AttributeDescription * desc)2167 acl_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2168 {
2169           AclSetCookie                  *cp = (AclSetCookie *)cookie;
2170           int                           rc = 0;
2171           LDAPURLDesc                   *ludp = NULL;
2172           Operation           op2 = { 0 };
2173           SlapReply           rs = {REP_RESULT};
2174           AttributeName                 anlist[ 2 ], *anlistp = NULL;
2175           int                           nattrs = 0;
2176           slap_callback                 cb = { NULL, acl_set_cb_gather, NULL, NULL };
2177           acl_set_gather_t    p = { 0 };
2178 
2179           /* this routine needs to return the bervals instead of
2180            * plain strings, since syntax is not known.  It should
2181            * also return the syntax or some "comparison cookie".
2182            */
2183           if ( strncasecmp( name->bv_val, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) {
2184                     return acl_set_gather2( cookie, name, desc );
2185           }
2186 
2187           rc = ldap_url_parse( name->bv_val, &ludp );
2188           if ( rc != LDAP_URL_SUCCESS ) {
2189                     Debug( LDAP_DEBUG_TRACE,
2190                               "%s acl_set_gather: unable to parse URL=\"%s\"\n",
2191                               cp->asc_op->o_log_prefix, name->bv_val );
2192 
2193                     rc = LDAP_PROTOCOL_ERROR;
2194                     goto url_done;
2195           }
2196 
2197           if ( ( ludp->lud_host && ludp->lud_host[0] ) || ludp->lud_exts )
2198           {
2199                     /* host part must be empty */
2200                     /* extensions parts must be empty */
2201                     Debug( LDAP_DEBUG_TRACE,
2202                               "%s acl_set_gather: host/exts must be absent in URL=\"%s\"\n",
2203                               cp->asc_op->o_log_prefix, name->bv_val );
2204 
2205                     rc = LDAP_PROTOCOL_ERROR;
2206                     goto url_done;
2207           }
2208 
2209           /* Grab the searchbase and see if an appropriate database can be found */
2210           ber_str2bv( ludp->lud_dn, 0, 0, &op2.o_req_dn );
2211           rc = dnNormalize( 0, NULL, NULL, &op2.o_req_dn,
2212                               &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
2213           BER_BVZERO( &op2.o_req_dn );
2214           if ( rc != LDAP_SUCCESS ) {
2215                     Debug( LDAP_DEBUG_TRACE,
2216                               "%s acl_set_gather: DN=\"%s\" normalize failed\n",
2217                               cp->asc_op->o_log_prefix, ludp->lud_dn );
2218 
2219                     goto url_done;
2220           }
2221 
2222           op2.o_bd = select_backend( &op2.o_req_ndn, 1 );
2223           if ( ( op2.o_bd == NULL ) || ( op2.o_bd->be_search == NULL ) ) {
2224                     Debug( LDAP_DEBUG_TRACE,
2225                               "%s acl_set_gather: no database could be selected for DN=\"%s\"\n",
2226                               cp->asc_op->o_log_prefix, op2.o_req_ndn.bv_val );
2227 
2228                     rc = LDAP_NO_SUCH_OBJECT;
2229                     goto url_done;
2230           }
2231 
2232           /* Grab the filter */
2233           if ( ludp->lud_filter ) {
2234                     ber_str2bv_x( ludp->lud_filter, 0, 0, &op2.ors_filterstr,
2235                                         cp->asc_op->o_tmpmemctx );
2236                     op2.ors_filter = str2filter_x( cp->asc_op, op2.ors_filterstr.bv_val );
2237                     if ( op2.ors_filter == NULL ) {
2238                               Debug( LDAP_DEBUG_TRACE,
2239                                         "%s acl_set_gather: unable to parse filter=\"%s\"\n",
2240                                         cp->asc_op->o_log_prefix, op2.ors_filterstr.bv_val );
2241 
2242                               rc = LDAP_PROTOCOL_ERROR;
2243                               goto url_done;
2244                     }
2245 
2246           } else {
2247                     op2.ors_filterstr = *slap_filterstr_objectClass_pres;
2248                     op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
2249           }
2250 
2251 
2252           /* Grab the scope */
2253           op2.ors_scope = ludp->lud_scope;
2254 
2255           /* Grap the attributes */
2256           if ( ludp->lud_attrs ) {
2257                     int i;
2258 
2259                     for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ )
2260                               ;
2261 
2262                     anlistp = slap_sl_calloc( sizeof( AttributeName ), nattrs + 2,
2263                                         cp->asc_op->o_tmpmemctx );
2264 
2265                     for ( i = 0, nattrs = 0; ludp->lud_attrs[ i ]; i++ ) {
2266                               struct berval                 name;
2267                               AttributeDescription          *desc = NULL;
2268                               const char                    *text = NULL;
2269 
2270                               ber_str2bv( ludp->lud_attrs[ i ], 0, 0, &name );
2271                               rc = slap_bv2ad( &name, &desc, &text );
2272                               if ( rc == LDAP_SUCCESS ) {
2273                                         anlistp[ nattrs ].an_name = name;
2274                                         anlistp[ nattrs ].an_desc = desc;
2275                                         nattrs++;
2276                               }
2277                     }
2278 
2279           } else {
2280                     anlistp = anlist;
2281           }
2282 
2283           anlistp[ nattrs ].an_name = desc->ad_cname;
2284           anlistp[ nattrs ].an_desc = desc;
2285 
2286           BER_BVZERO( &anlistp[ nattrs + 1 ].an_name );
2287 
2288           p.cookie = cookie;
2289 
2290           op2.o_hdr = cp->asc_op->o_hdr;
2291           op2.o_tag = LDAP_REQ_SEARCH;
2292           op2.o_ndn = op2.o_bd->be_rootndn;
2293           op2.o_callback = &cb;
2294           slap_op_time( &op2.o_time, &op2.o_tincr );
2295           op2.o_do_not_cache = 1;
2296           op2.o_is_auth_check = 0;
2297           ber_dupbv_x( &op2.o_req_dn, &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
2298           op2.ors_slimit = SLAP_NO_LIMIT;
2299           op2.ors_tlimit = SLAP_NO_LIMIT;
2300           op2.ors_attrs = anlistp;
2301           op2.ors_attrsonly = 0;
2302           op2.o_private = cp->asc_op->o_private;
2303           op2.o_extra = cp->asc_op->o_extra;
2304 
2305           cb.sc_private = &p;
2306 
2307           rc = op2.o_bd->be_search( &op2, &rs );
2308           if ( rc != 0 ) {
2309                     goto url_done;
2310           }
2311 
2312 url_done:;
2313           if ( op2.ors_filter && op2.ors_filter != slap_filter_objectClass_pres ) {
2314                     filter_free_x( cp->asc_op, op2.ors_filter, 1 );
2315           }
2316           if ( !BER_BVISNULL( &op2.o_req_ndn ) ) {
2317                     slap_sl_free( op2.o_req_ndn.bv_val, cp->asc_op->o_tmpmemctx );
2318           }
2319           if ( !BER_BVISNULL( &op2.o_req_dn ) ) {
2320                     slap_sl_free( op2.o_req_dn.bv_val, cp->asc_op->o_tmpmemctx );
2321           }
2322           if ( ludp ) {
2323                     ldap_free_urldesc( ludp );
2324           }
2325           if ( anlistp && anlistp != anlist ) {
2326                     slap_sl_free( anlistp, cp->asc_op->o_tmpmemctx );
2327           }
2328 
2329           return p.bvals;
2330 }
2331 
2332 BerVarray
acl_set_gather2(SetCookie * cookie,struct berval * name,AttributeDescription * desc)2333 acl_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2334 {
2335           AclSetCookie        *cp = (AclSetCookie *)cookie;
2336           BerVarray bvals = NULL;
2337           struct berval       ndn;
2338           int                 rc = 0;
2339 
2340           /* this routine needs to return the bervals instead of
2341            * plain strings, since syntax is not known.  It should
2342            * also return the syntax or some "comparison cookie".
2343            */
2344           rc = dnNormalize( 0, NULL, NULL, name, &ndn, cp->asc_op->o_tmpmemctx );
2345           if ( rc == LDAP_SUCCESS ) {
2346                     if ( desc == slap_schema.si_ad_entryDN ) {
2347                               bvals = (BerVarray)slap_sl_malloc( sizeof( BerValue ) * 2,
2348                                                   cp->asc_op->o_tmpmemctx );
2349                               bvals[ 0 ] = ndn;
2350                               BER_BVZERO( &bvals[ 1 ] );
2351                               BER_BVZERO( &ndn );
2352 
2353                     } else {
2354                               backend_attribute( cp->asc_op,
2355                                         cp->asc_e, &ndn, desc, &bvals, ACL_NONE );
2356                     }
2357 
2358                     if ( !BER_BVISNULL( &ndn ) ) {
2359                               slap_sl_free( ndn.bv_val, cp->asc_op->o_tmpmemctx );
2360                     }
2361           }
2362 
2363           return bvals;
2364 }
2365 
2366 int
acl_match_set(struct berval * subj,Operation * op,Entry * e,struct berval * default_set_attribute)2367 acl_match_set (
2368           struct berval *subj,
2369           Operation *op,
2370           Entry *e,
2371           struct berval *default_set_attribute )
2372 {
2373           struct berval       set = BER_BVNULL;
2374           int                 rc = 0;
2375           AclSetCookie        cookie;
2376 
2377           if ( default_set_attribute == NULL ) {
2378                     set = *subj;
2379 
2380           } else {
2381                     struct berval                 subjdn, ndn = BER_BVNULL;
2382                     struct berval                 setat;
2383                     BerVarray           bvals = NULL;
2384                     const char                    *text;
2385                     AttributeDescription          *desc = NULL;
2386 
2387                     /* format of string is "entry/setAttrName" */
2388                     if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
2389                               return 0;
2390                     }
2391 
2392                     if ( acl_get_part( subj, 1, '/', &setat ) < 0 ) {
2393                               setat = *default_set_attribute;
2394                     }
2395 
2396                     /*
2397                      * NOTE: dnNormalize honors the ber_len field
2398                      * as the length of the dn to be normalized
2399                      */
2400                     if ( slap_bv2ad( &setat, &desc, &text ) == LDAP_SUCCESS ) {
2401                               if ( dnNormalize( 0, NULL, NULL, &subjdn, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
2402                               {
2403                                         backend_attribute( op, e, &ndn, desc, &bvals, ACL_NONE );
2404                                         if ( bvals != NULL && !BER_BVISNULL( &bvals[0] ) ) {
2405                                                   int       i;
2406 
2407                                                   set = bvals[0];
2408                                                   BER_BVZERO( &bvals[0] );
2409                                                   for ( i = 1; !BER_BVISNULL( &bvals[i] ); i++ )
2410                                                             /* count */ ;
2411                                                   bvals[0].bv_val = bvals[i-1].bv_val;
2412                                                   BER_BVZERO( &bvals[i-1] );
2413                                         }
2414                                         ber_bvarray_free_x( bvals, op->o_tmpmemctx );
2415                                         slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
2416                               }
2417                     }
2418           }
2419 
2420           if ( !BER_BVISNULL( &set ) ) {
2421                     cookie.asc_op = op;
2422                     cookie.asc_e = e;
2423                     rc = ( slap_set_filter(
2424                               acl_set_gather,
2425                               (SetCookie *)&cookie, &set,
2426                               &op->o_ndn, &e->e_nname, NULL ) > 0 );
2427                     if ( set.bv_val != subj->bv_val ) {
2428                               slap_sl_free( set.bv_val, op->o_tmpmemctx );
2429                     }
2430           }
2431 
2432           return(rc);
2433 }
2434 
2435 #ifdef SLAP_DYNACL
2436 
2437 /*
2438  * dynamic ACL infrastructure
2439  */
2440 static slap_dynacl_t          *da_list = NULL;
2441 
2442 int
slap_dynacl_register(slap_dynacl_t * da)2443 slap_dynacl_register( slap_dynacl_t *da )
2444 {
2445           slap_dynacl_t       *tmp;
2446 
2447           for ( tmp = da_list; tmp; tmp = tmp->da_next ) {
2448                     if ( strcasecmp( da->da_name, tmp->da_name ) == 0 ) {
2449                               break;
2450                     }
2451           }
2452 
2453           if ( tmp != NULL ) {
2454                     return -1;
2455           }
2456 
2457           if ( da->da_mask == NULL ) {
2458                     return -1;
2459           }
2460 
2461           da->da_private = NULL;
2462           da->da_next = da_list;
2463           da_list = da;
2464 
2465           return 0;
2466 }
2467 
2468 static slap_dynacl_t *
slap_dynacl_next(slap_dynacl_t * da)2469 slap_dynacl_next( slap_dynacl_t *da )
2470 {
2471           if ( da ) {
2472                     return da->da_next;
2473           }
2474           return da_list;
2475 }
2476 
2477 slap_dynacl_t *
slap_dynacl_get(const char * name)2478 slap_dynacl_get( const char *name )
2479 {
2480           slap_dynacl_t       *da;
2481 
2482           for ( da = slap_dynacl_next( NULL ); da; da = slap_dynacl_next( da ) ) {
2483                     if ( strcasecmp( da->da_name, name ) == 0 ) {
2484                               break;
2485                     }
2486           }
2487 
2488           return da;
2489 }
2490 #endif /* SLAP_DYNACL */
2491 
2492 /*
2493  * statically built-in dynamic ACL initialization
2494  */
2495 static int (*acl_init_func[])( void ) = {
2496 #ifdef SLAP_DYNACL
2497           /* TODO: remove when ACI will only be dynamic */
2498 #if SLAPD_ACI_ENABLED == SLAPD_MOD_STATIC
2499           dynacl_aci_init,
2500 #endif /* SLAPD_ACI_ENABLED */
2501 #endif /* SLAP_DYNACL */
2502 
2503           NULL
2504 };
2505 
2506 int
acl_init(void)2507 acl_init( void )
2508 {
2509           int       i, rc;
2510 
2511           for ( i = 0; acl_init_func[ i ] != NULL; i++ ) {
2512                     rc = (*(acl_init_func[ i ]))();
2513                     if ( rc != 0 ) {
2514                               return rc;
2515                     }
2516           }
2517 
2518           return 0;
2519 }
2520 
2521 int
acl_string_expand(struct berval * bv,struct berval * pat,struct berval * dn_matches,struct berval * val_matches,AclRegexMatches * matches)2522 acl_string_expand(
2523           struct berval       *bv,
2524           struct berval       *pat,
2525           struct berval       *dn_matches,
2526           struct berval       *val_matches,
2527           AclRegexMatches     *matches)
2528 {
2529           ber_len_t size;
2530           char   *sp;
2531           char   *dp;
2532           int       flag;
2533           enum { DN_FLAG, VAL_FLAG } tflag;
2534 
2535           size = 0;
2536           bv->bv_val[0] = '\0';
2537           bv->bv_len--; /* leave space for lone $ */
2538 
2539           flag = 0;
2540           tflag = DN_FLAG;
2541           for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
2542                     sp < pat->bv_val + pat->bv_len ; sp++ )
2543           {
2544                     /* did we previously see a $ */
2545                     if ( flag ) {
2546                               if ( flag == 1 && *sp == '$' ) {
2547                                         *dp++ = '$';
2548                                         size++;
2549                                         flag = 0;
2550                                         tflag = DN_FLAG;
2551 
2552                               } else if ( flag == 2 && *sp == 'v' /*'}'*/) {
2553                                         tflag = VAL_FLAG;
2554 
2555                               } else if ( flag == 2 && *sp == 'd' /*'}'*/) {
2556                                         tflag = DN_FLAG;
2557 
2558                               } else if ( flag == 1 && *sp == '{' /*'}'*/) {
2559                                         flag = 2;
2560 
2561                               } else if ( *sp >= '0' && *sp <= '9' ) {
2562                                         int       nm;
2563                                         regmatch_t *m;
2564                                         char *data;
2565                                         int       n;
2566                                         int       i;
2567                                         int       l;
2568 
2569                                         n = *sp - '0';
2570 
2571                                         if ( flag == 2 ) {
2572                                                   for ( sp++; *sp != '\0' && *sp != /*'{'*/ '}'; sp++ ) {
2573                                                             if ( *sp >= '0' && *sp <= '9' ) {
2574                                                                       n = 10*n + ( *sp - '0' );
2575                                                             }
2576                                                   }
2577 
2578                                                   if ( *sp != /*'{'*/ '}' ) {
2579                                                             /* FIXME: error */
2580                                                             return 1;
2581                                                   }
2582                                         }
2583 
2584                                         switch (tflag) {
2585                                         case DN_FLAG:
2586                                                   nm = matches->dn_count;
2587                                                   m = matches->dn_data;
2588                                                   data = dn_matches ? dn_matches->bv_val : NULL;
2589                                                   break;
2590                                         case VAL_FLAG:
2591                                                   nm = matches->val_count;
2592                                                   m = matches->val_data;
2593                                                   data = val_matches ? val_matches->bv_val : NULL;
2594                                                   break;
2595                                         default:
2596                                                   assert( 0 );
2597                                         }
2598                                         if ( n >= nm ) {
2599                                                   /* FIXME: error */
2600                                                   return 1;
2601                                         }
2602                                         if ( data == NULL ) {
2603                                                   /* FIXME: error */
2604                                                   return 1;
2605                                         }
2606 
2607                                         *dp = '\0';
2608                                         i = m[n].rm_so;
2609                                         l = m[n].rm_eo;
2610 
2611                                         for ( ; size < bv->bv_len && i < l; size++, i++ ) {
2612                                                   *dp++ = data[i];
2613                                         }
2614                                         *dp = '\0';
2615 
2616                                         flag = 0;
2617                                         tflag = DN_FLAG;
2618                               }
2619                     } else {
2620                               if (*sp == '$') {
2621                                         flag = 1;
2622                               } else {
2623                                         *dp++ = *sp;
2624                                         size++;
2625                               }
2626                     }
2627           }
2628 
2629           if ( flag ) {
2630                     /* must have ended with a single $ */
2631                     *dp++ = '$';
2632                     size++;
2633           }
2634 
2635           *dp = '\0';
2636           bv->bv_len = size;
2637 
2638           Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: pattern:  %.*s\n", (int)pat->bv_len, pat->bv_val );
2639           Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: expanded: %s\n", bv->bv_val );
2640 
2641           return 0;
2642 }
2643 
2644 static int
regex_matches(struct berval * pat,char * str,struct berval * dn_matches,struct berval * val_matches,AclRegexMatches * matches)2645 regex_matches(
2646           struct berval       *pat,               /* pattern to expand and match against */
2647           char                *str,               /* string to match against pattern */
2648           struct berval       *dn_matches,        /* buffer with $N expansion variables from DN */
2649           struct berval       *val_matches,       /* buffer with $N expansion variables from val */
2650           AclRegexMatches     *matches  /* offsets in buffer for $N expansion variables */
2651 )
2652 {
2653           regex_t re;
2654           char newbuf[ACL_BUF_SIZE];
2655           struct berval bv;
2656           int       rc;
2657 
2658           bv.bv_len = sizeof( newbuf ) - 1;
2659           bv.bv_val = newbuf;
2660 
2661           if (str == NULL) {
2662                     str = "";
2663           };
2664 
2665           if ( acl_string_expand( &bv, pat, dn_matches, val_matches, matches )) {
2666                     Debug( LDAP_DEBUG_TRACE,
2667                               "expand( \"%s\", \"%s\") failed\n",
2668                               pat->bv_val, str );
2669                     return( 0 );
2670           }
2671           rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
2672           if ( rc ) {
2673                     char error[ACL_BUF_SIZE];
2674                     regerror( rc, &re, error, sizeof( error ) );
2675 
2676                     Debug( LDAP_DEBUG_TRACE,
2677                         "compile( \"%s\", \"%s\") failed %s\n",
2678                               pat->bv_val, str, error );
2679                     return( 0 );
2680           }
2681 
2682           rc = regexec( &re, str, 0, NULL, 0 );
2683           regfree( &re );
2684 
2685           Debug( LDAP_DEBUG_TRACE,
2686               "=> regex_matches: string:           %s\n", str );
2687           Debug( LDAP_DEBUG_TRACE,
2688               "=> regex_matches: rc: %d %s\n",
2689                     rc, !rc ? "matches" : "no matches" );
2690           return( !rc );
2691 }
2692 
2693