]> git.sur5r.net Git - openldap/blob - servers/slapd/acl.c
31b1b80e9ca256c638bb4a4ab3809d6f7529609d
[openldap] / servers / slapd / acl.c
1 /* acl.c - routines to parse and check acl's */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/regex.h>
13 #include <ac/socket.h>
14 #include <ac/string.h>
15
16 #include "slap.h"
17
18 static AccessControl * acl_get(
19         AccessControl *ac, int *count,
20         Backend *be, Operation *op,
21         Entry *e,
22 #ifdef SLAPD_SCHEMA_NOT_COMPAT
23         AttributeDescription *desc,
24 #else
25         const char *desc,
26 #endif
27         int nmatches, regmatch_t *matches );
28
29 static slap_control_t acl_mask(
30         AccessControl *ac, slap_access_mask_t *mask,
31         Backend *be, Connection *conn, Operation *op,
32         Entry *e,
33 #ifdef SLAPD_SCHEMA_NOT_COMPAT
34         AttributeDescription *desc,
35 #else
36         const char *desc,
37 #endif
38         struct berval *val,
39         regmatch_t *matches );
40
41 #ifdef SLAPD_ACI_ENABLED
42 static int aci_mask(
43         Backend *be,
44         Operation *op,
45         Entry *e,
46 #ifdef SLAPD_SCHEMA_NOT_COMPAT
47         AttributeDescription *desc,
48 #else
49         const char *desc,
50 #endif
51         struct berval *val,
52         struct berval *aci,
53         regmatch_t *matches,
54         slap_access_t *grant,
55         slap_access_t *deny );
56 #endif
57
58 static int      regex_matches(
59         char *pat, char *str, char *buf, regmatch_t *matches);
60 static void     string_expand(
61         char *newbuf, int bufsiz, char *pattern,
62         char *match, regmatch_t *matches);
63
64
65 /*
66  * access_allowed - check whether op->o_ndn is allowed the requested access
67  * to entry e, attribute attr, value val.  if val is null, access to
68  * the whole attribute is assumed (all values).
69  *
70  * This routine loops through all access controls and calls
71  * acl_mask() on each applicable access control.
72  * The loop exits when a definitive answer is reached or
73  * or no more controls remain.
74  *
75  * returns:
76  *              0       access denied
77  *              1       access granted
78  */
79
80 int
81 access_allowed(
82     Backend             *be,
83     Connection          *conn,
84     Operation           *op,
85     Entry               *e,
86 #ifdef SLAPD_SCHEMA_NOT_COMPAT
87         AttributeDescription    *desc,
88 #else
89     const char          *desc,
90 #endif
91     struct berval       *val,
92     slap_access_t       access )
93 {
94         int                             count;
95         AccessControl   *a;
96 #ifdef LDAP_DEBUG
97         char accessmaskbuf[ACCESSMASK_MAXLEN];
98 #endif
99         slap_access_mask_t mask;
100         slap_control_t control;
101
102 #ifdef SLAPD_SCHEMA_NOT_COMPAT
103         const char *attr = desc ? desc->ad_cname->bv_val : NULL;
104 #else
105     const char *attr = desc;
106 #endif
107
108         regmatch_t       matches[MAXREMATCHES];
109
110         Debug( LDAP_DEBUG_ACL,
111                 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
112             access2str( access ),
113                 e->e_dn, attr );
114
115         assert( be != NULL );
116         assert( e != NULL );
117         assert( attr != NULL );
118         assert( access > ACL_NONE );
119
120         /* grant database root access */
121         if ( be != NULL && be_isroot( be, op->o_ndn ) ) {
122                 Debug( LDAP_DEBUG_ACL,
123                     "<= root access granted\n",
124                         0, 0, 0 );
125                 return 1;
126         }
127
128         /*
129          * no-user-modification operational attributes are ignored
130          * by ACL_WRITE checking as any found here are not provided
131          * by the user
132          */
133 #ifdef SLAPD_SCHEMA_NOT_COMPAT
134         if ( access >= ACL_WRITE && is_at_no_user_mod( desc->ad_type ) )
135 #else
136         if ( access >= ACL_WRITE && oc_check_op_no_usermod_attr( attr ) )
137 #endif
138         {
139                 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
140                         " %s access granted\n",
141                         attr, 0, 0 );
142                 return 1;
143         }
144
145         /* use backend default access if no backend acls */
146         if( be != NULL && be->be_acl == NULL ) {
147                 Debug( LDAP_DEBUG_ACL,
148                         "=> access_allowed: backend default %s access %s to \"%s\"\n",
149                         access2str( access ),
150                         be->be_dfltaccess >= access ? "granted" : "denied", op->o_dn );
151
152                 return be->be_dfltaccess >= access;
153
154 #ifdef notdef
155         /* be is always non-NULL */
156         /* use global default access if no global acls */
157         } else if ( be == NULL && global_acl == NULL ) {
158                 Debug( LDAP_DEBUG_ACL,
159                         "=> access_allowed: global default %s access %s to \"%s\"\n",
160                         access2str( access ),
161                         global_default_access >= access ? "granted" : "denied", op->o_dn );
162
163                 return global_default_access >= access;
164 #endif
165         }
166
167         ACL_INIT(mask);
168         memset(matches, 0, sizeof(matches));
169         
170         control = ACL_BREAK;
171         a = NULL;
172         count = 0;
173
174         while( a = acl_get( a, &count, be, op, e, desc, MAXREMATCHES, matches ) )
175         {
176                 int i;
177
178                 for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
179                         Debug( LDAP_DEBUG_ACL, "=> match[%d]: %d %d ", i,
180                                (int)matches[i].rm_so, (int)matches[i].rm_eo );
181
182                         if( matches[i].rm_so <= matches[0].rm_eo ) {
183                                 int n;
184                                 for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
185                                         Debug( LDAP_DEBUG_ACL, "%c", e->e_ndn[n], 0, 0 );
186                                 }
187                         }
188                         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
189                 }
190
191                 control = acl_mask( a, &mask, be, conn, op,
192                         e, desc, val, matches );
193
194                 if ( control != ACL_BREAK ) {
195                         break;
196                 }
197
198                 memset(matches, 0, sizeof(matches));
199         }
200
201         if ( ACL_IS_INVALID( mask ) ) {
202                 Debug( LDAP_DEBUG_ACL,
203                         "=> access_allowed: \"%s\" (%s) invalid!\n",
204                         e->e_dn, attr, 0 );
205                 ACL_INIT( mask );
206
207         } else if ( control == ACL_BREAK ) {
208                 Debug( LDAP_DEBUG_ACL,
209                         "=> access_allowed: no more rules\n", 0, 0, 0);
210                 ACL_INIT( mask );
211         }
212
213         Debug( LDAP_DEBUG_ACL,
214                 "=> access_allowed: %s access %s by %s\n",
215                 access2str( access ),
216                 ACL_GRANT(mask, access) ? "granted" : "denied",
217                 accessmask2str( mask, accessmaskbuf ) );
218
219         return ACL_GRANT(mask, access);
220 }
221
222 /*
223  * acl_get - return the acl applicable to entry e, attribute
224  * attr.  the acl returned is suitable for use in subsequent calls to
225  * acl_access_allowed().
226  */
227
228 static AccessControl *
229 acl_get(
230         AccessControl *a,
231         int                     *count,
232     Backend             *be,
233     Operation   *op,
234     Entry               *e,
235 #ifdef SLAPD_SCHEMA_NOT_COMPAT
236         AttributeDescription *desc,
237 #else
238     const char  *desc,
239 #endif
240     int                 nmatch,
241     regmatch_t  *matches )
242 {
243         const char *attr;
244         assert( e != NULL );
245         assert( count != NULL );
246
247 #ifdef SLAPD_SCHEMA_NOT_COMPAT
248         attr = desc ? desc->ad_cname->bv_val : NULL;
249 #else
250         attr = desc;
251 #endif
252
253         if( a == NULL ) {
254                 if( be == NULL ) {
255                         a = global_acl;
256                 } else {
257                         a = be->be_acl;
258                 }
259
260                 assert( a != NULL );
261
262         } else {
263                 a = a->acl_next;
264         }
265
266         for ( ; a != NULL; a = a->acl_next ) {
267                 (*count) ++;
268
269                 if (a->acl_dn_pat != NULL) {
270                         Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n", 
271                                 *count, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub );
272
273                         if (regexec(&a->acl_dn_re, e->e_ndn, nmatch, matches, 0)) {
274                                 continue;
275
276                         } else {
277                                 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
278                                         *count, 0, 0);
279                         }
280                 }
281
282                 if ( a->acl_filter != NULL ) {
283                         ber_int_t rc = test_filter( NULL, NULL, NULL, e, a->acl_filter );
284                         if ( rc != LDAP_COMPARE_TRUE ) {
285                                 continue;
286                         }
287                 }
288
289         Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] check attr %s\n",
290                         *count, attr, 0);
291
292                 if ( attr == NULL || a->acl_attrs == NULL ||
293                         ad_inlist( desc, a->acl_attrs ) )
294                 {
295                         Debug( LDAP_DEBUG_ACL,
296                                 "<= acl_get: [%d] acl %s attr: %s\n",
297                                 *count, e->e_dn, attr );
298                         return a;
299                 }
300                 matches[0].rm_so = matches[0].rm_eo = -1;
301         }
302
303         Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
304         return( NULL );
305 }
306
307
308 /*
309  * acl_mask - modifies mask based upon the given acl and the
310  * requested access to entry e, attribute attr, value val.  if val
311  * is null, access to the whole attribute is assumed (all values).
312  *
313  * returns      0       access NOT allowed
314  *              1       access allowed
315  */
316
317 static slap_control_t
318 acl_mask(
319     AccessControl       *a,
320         slap_access_mask_t *mask,
321     Backend             *be,
322     Connection  *conn,
323     Operation   *op,
324     Entry               *e,
325 #ifdef SLAPD_SCHEMA_NOT_COMPAT
326         AttributeDescription *desc,
327 #else
328     const char  *desc,
329 #endif
330     struct berval       *val,
331         regmatch_t      *matches
332 )
333 {
334         int             i;
335         Access  *b;
336 #ifdef LDAP_DEBUG
337         char accessmaskbuf[ACCESSMASK_MAXLEN];
338 #endif
339 #ifdef SLAPD_SCHEMA_NOT_COMPAT
340         const char *attr = desc ? desc->ad_cname->bv_val : NULL;
341 #else
342         const char *attr = desc;
343 #endif
344
345         assert( a != NULL );
346         assert( mask != NULL );
347
348         Debug( LDAP_DEBUG_ACL,
349                 "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
350                 e->e_dn, attr, 0 );
351
352         Debug( LDAP_DEBUG_ACL,
353                 "=> acl_mask: to value \"%s\" by \"%s\", (%s) \n",
354                 val ? val->bv_val : "*",
355                 op->o_ndn ?  op->o_ndn : "",
356                 accessmask2str( *mask, accessmaskbuf ) );
357
358         for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
359                 slap_access_mask_t oldmask, modmask;
360
361                 ACL_INVALIDATE( modmask );
362
363                 /* AND <who> clauses */
364                 if ( b->a_dn_pat != NULL ) {
365                         Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
366                                 b->a_dn_pat, 0, 0);
367                         /*
368                          * if access applies to the entry itself, and the
369                          * user is bound as somebody in the same namespace as
370                          * the entry, OR the given dn matches the dn pattern
371                          */
372                         if ( strcmp( b->a_dn_pat, "anonymous" ) == 0 ) {
373                                 if (op->o_ndn != NULL && op->o_ndn[0] != '\0' ) {
374                                         continue;
375                                 }
376
377                         } else if ( strcmp( b->a_dn_pat, "users" ) == 0 ) {
378                                 if (op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
379                                         continue;
380                                 }
381
382                         } else if ( strcmp( b->a_dn_pat, "self" ) == 0 ) {
383                                 if( op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
384                                         continue;
385                                 }
386                                 
387                                 if ( e->e_dn == NULL || strcmp( e->e_ndn, op->o_ndn ) != 0 ) {
388                                         continue;
389                                 }
390
391                         } else if ( strcmp( b->a_dn_pat, "*" ) != 0 ) {
392                                 int ret = regex_matches( b->a_dn_pat,
393                                         op->o_ndn, e->e_ndn, matches );
394
395                                 if( ret == 0 ) {
396                                         continue;
397                                 }
398                         }
399                 }
400
401                 if ( b->a_sockurl_pat != NULL ) {
402                         Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
403                                 b->a_sockurl_pat, 0, 0 );
404
405                         if ( strcmp( b->a_sockurl_pat, "*" ) != 0 &&
406                                 !regex_matches( b->a_sockurl_pat, conn->c_listener_url,
407                                 e->e_ndn, matches ) ) 
408                         {
409                                 continue;
410                         }
411                 }
412
413                 if ( b->a_domain_pat != NULL ) {
414                         Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
415                                 b->a_domain_pat, 0, 0 );
416
417                         if ( strcmp( b->a_domain_pat, "*" ) != 0 &&
418                                 !regex_matches( b->a_domain_pat, conn->c_peer_domain,
419                                 e->e_ndn, matches ) ) 
420                         {
421                                 continue;
422                         }
423                 }
424
425                 if ( b->a_peername_pat != NULL ) {
426                         Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
427                                 b->a_peername_pat, 0, 0 );
428
429                         if ( strcmp( b->a_peername_pat, "*" ) != 0 &&
430                                 !regex_matches( b->a_peername_pat, conn->c_peer_name,
431                                 e->e_ndn, matches ) )
432                         {
433                                 continue;
434                         }
435                 }
436
437                 if ( b->a_sockname_pat != NULL ) {
438                         Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
439                                 b->a_sockname_pat, 0, 0 );
440
441                         if ( strcmp( b->a_sockname_pat, "*" ) != 0 &&
442                                 !regex_matches( b->a_sockname_pat, conn->c_sock_name,
443                                 e->e_ndn, matches ) )
444                         {
445                                 continue;
446                         }
447                 }
448
449                 if ( b->a_dn_at != NULL && op->o_ndn != NULL ) {
450                         Attribute       *at;
451                         struct berval   bv;
452 #ifdef SLAPD_SCHEMA_NOT_COMPAT
453                         int match;
454                         const char *text;
455                         const char *desc = b->a_dn_at->ad_cname->bv_val;
456 #else
457                         const char *desc = b->a_dn_at;
458 #endif
459
460                         Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n",
461                                 b->a_dn_at, 0, 0);
462
463                         bv.bv_val = op->o_ndn;
464                         bv.bv_len = strlen( bv.bv_val );
465
466                         /* see if asker is listed in dnattr */
467 #ifdef SLAPD_SCHEMA_NOT_COMPAT
468                         for( at = attrs_find( e->e_attrs, b->a_dn_at );
469                                 at == NULL;
470                                 at = attrs_find( e->e_attrs->a_next, b->a_dn_at ) )
471                         {
472                                 if( value_find( b->a_dn_at, at->a_vals, &bv ) == 0 ) {
473                                         /* found it */
474                                         match = 1;
475                                         break;
476                                 }
477                         }
478
479                         if( match ) {
480                                 if ( b->a_dn_self && (val == NULL
481                                         || value_match( &match, b->a_dn_at,
482                                                 b->a_dn_at->ad_type->sat_equality, val, &bv, &text ) )
483                                                 != LDAP_SUCCESS
484                                         || match )
485                                 {
486                                         continue;
487                                 }
488                         } else if ( ! b->a_dn_self || val == NULL
489                                 || value_match( &match, b->a_dn_at,
490                                         b->a_dn_at->ad_type->sat_equality, val, &bv, &text )
491                                         != LDAP_SUCCESS
492                                 || match )
493                         {
494                                 continue;
495                         }
496 #else
497                         /* see if asker is listed in dnattr */
498                         if ( (at = attr_find( e->e_attrs, b->a_dn_at )) != NULL &&
499                                 value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0 )
500                         {
501                                 if ( b->a_dn_self && (val == NULL
502                                         || value_cmp( &bv, val, at->a_syntax, 2 ) ) )
503                                 {
504                                         continue;
505                                 }
506
507                         /* asker not listed in dnattr - check for self access */
508                         } else if ( ! b->a_dn_self || val == NULL
509                                 || value_cmp( &bv, val, at->a_syntax, 2 ) != 0 )
510                         {
511                                 continue;
512                         }
513 #endif
514                 }
515
516                 if ( b->a_group_pat != NULL && op->o_ndn != NULL ) {
517                         char buf[1024];
518
519                         /* b->a_group is an unexpanded entry name, expanded it should be an 
520                          * entry with objectclass group* and we test to see if odn is one of
521                          * the values in the attribute group
522                          */
523                         /* see if asker is listed in dnattr */
524                         string_expand(buf, sizeof(buf), b->a_group_pat, e->e_ndn, matches);
525                         if ( dn_normalize(buf) == NULL ) {
526                                 /* did not expand to a valid dn */
527                                 continue;
528                         }
529
530                         if (backend_group(be, e, buf, op->o_ndn,
531                                 b->a_group_oc, b->a_group_at) != 0)
532                         {
533                                 continue;
534                         }
535                 }
536
537 #ifdef SLAPD_ACI_ENABLED
538                 if ( b->a_aci_at != NULL ) {
539                         Attribute       *at;
540                         slap_access_t grant, deny, tgrant, tdeny;
541
542                         /* this case works different from the others above.
543                          * since aci's themselves give permissions, we need
544                          * to first check b->a_mask, the ACL's access level.
545                          */
546
547                         if( op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
548                                 continue;
549                         }
550
551                         if ( e->e_dn == NULL ) {
552                                 continue;
553                         }
554
555                         /* first check if the right being requested
556                          * is allowed by the ACL clause.
557                          */
558                         if ( ! ACL_GRANT( b->a_mask, *mask ) ) {
559                                 continue;
560                         }
561
562                         /* get the aci attribute */
563                         at = attr_find( e->e_attrs, b->a_aci_at );
564                         if ( at == NULL ) {
565                                 continue;
566                         }
567
568                         /* start out with nothing granted, nothing denied */
569                         ACL_INIT(tgrant);
570                         ACL_INIT(tdeny);
571
572                         /* the aci is an multi-valued attribute.  The
573                          * rights are determined by OR'ing the individual
574                          * rights given by the acis.
575                          */
576                         for ( i = 0; at->a_vals[i] != NULL; i++ ) {
577                                 if (aci_mask( be, op,
578                                         e, desc, val, at->a_vals[i],
579                                         matches, &grant, &deny ) != 0)
580                                 {
581                                         tgrant |= grant;
582                                         tdeny |= deny;
583                                 }
584                         }
585
586                         /* remove anything that the ACL clause does not allow */
587                         tgrant &= b->a_mask & ACL_PRIV_MASK;
588                         tdeny &= ACL_PRIV_MASK;
589
590                         /* see if we have anything to contribute */
591                         if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) { 
592                                 continue;
593                         }
594
595                         /* this could be improved by changing acl_mask so that it can deal with
596                          * by clauses that return grant/deny pairs.  Right now, it does either
597                          * additive or subtractive rights, but not both at the same time.  So,
598                          * we need to combine the grant/deny pair into a single rights mask in
599                          * a smart way:  if either grant or deny is "empty", then we use the
600                          * opposite as is, otherwise we remove any denied rights from the grant
601                          * rights mask and construct an additive mask.
602                          */
603                         if (ACL_IS_INVALID(tdeny)) {
604                                 modmask = tgrant | ACL_PRIV_ADDITIVE;
605
606                         } else if (ACL_IS_INVALID(tgrant)) {
607                                 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
608
609                         } else {
610                                 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
611                         }
612
613                 } else
614 #endif
615                 {
616                         modmask = b->a_mask;
617                 }
618
619
620                 Debug( LDAP_DEBUG_ACL,
621                         "<= acl_mask: [%d] applying %s (%s)\n",
622                         i, accessmask2str( modmask, accessmaskbuf ), 
623                         b->a_type == ACL_CONTINUE
624                                 ? "continue"
625                                 : b->a_type == ACL_BREAK
626                                         ? "break"
627                                         : "stop" );
628
629                 /* save old mask */
630                 oldmask = *mask;
631
632                 if( ACL_IS_ADDITIVE(modmask) ) {
633                         /* add privs */
634                         ACL_PRIV_SET( *mask, modmask );
635
636                         /* cleanup */
637                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
638
639                 } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
640                         /* substract privs */
641                         ACL_PRIV_CLR( *mask, modmask );
642
643                         /* cleanup */
644                         ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
645
646                 } else {
647                         /* assign privs */
648                         *mask = modmask;
649                 }
650
651                 Debug( LDAP_DEBUG_ACL,
652                         "<= acl_mask: [%d] mask: %s\n",
653                         i, accessmask2str(*mask, accessmaskbuf), 0 );
654
655                 if( b->a_type == ACL_CONTINUE ) {
656                         continue;
657
658                 } else if ( b->a_type == ACL_BREAK ) {
659                         return ACL_BREAK;
660
661                 } else {
662                         return ACL_STOP;
663                 }
664         }
665
666         Debug( LDAP_DEBUG_ACL,
667                 "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
668                 accessmask2str(*mask, accessmaskbuf), 0, 0 );
669         return ACL_STOP;
670 }
671
672 /*
673  * acl_check_modlist - check access control on the given entry to see if
674  * it allows the given modifications by the user associated with op.
675  * returns      1       if mods allowed ok
676  *                      0       mods not allowed
677  */
678
679 int
680 acl_check_modlist(
681     Backend     *be,
682     Connection  *conn,
683     Operation   *op,
684     Entry       *e,
685     Modifications       *mlist
686 )
687 {
688         int             i;
689
690         assert( be != NULL );
691
692         /* short circuit root database access */
693         if ( be_isroot( be, op->o_ndn ) ) {
694                 Debug( LDAP_DEBUG_ACL,
695                         "<= acl_access_allowed: granted to database root\n",
696                     0, 0, 0 );
697                 return 1;
698         }
699
700         /* use backend default access if no backend acls */
701         if( be != NULL && be->be_acl == NULL ) {
702                 Debug( LDAP_DEBUG_ACL,
703                         "=> access_allowed: backend default %s access %s to \"%s\"\n",
704                         access2str( ACL_WRITE ),
705                         be->be_dfltaccess >= ACL_WRITE ? "granted" : "denied", op->o_dn );
706
707                 return be->be_dfltaccess >= ACL_WRITE;
708
709 #ifdef notdef
710         /* be is always non-NULL */
711         /* use global default access if no global acls */
712         } else if ( be == NULL && global_acl == NULL ) {
713                 Debug( LDAP_DEBUG_ACL,
714                         "=> access_allowed: global default %s access %s to \"%s\"\n",
715                         access2str( ACL_WRITE ),
716                         global_default_access >= ACL_WRITE ? "granted" : "denied", op->o_dn );
717
718                 return global_default_access >= ACL_WRITE;
719 #endif
720         }
721
722         for ( ; mlist != NULL; mlist = mlist->sml_next ) {
723                 /*
724                  * no-user-modification operational attributes are ignored
725                  * by ACL_WRITE checking as any found here are not provided
726                  * by the user
727                  */
728 #ifdef SLAPD_SCHEMA_NOT_COMPAT
729                 if ( is_at_no_user_mod( mlist->sml_desc->ad_type ) ) {
730                         Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
731                                 " modify access granted\n",
732                                 mlist->sml_desc->ad_cname->bv_val, 0, 0 );
733                         continue;
734                 }
735 #else
736                 if ( oc_check_op_no_usermod_attr( mlist->sml_type ) ) {
737                         Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
738                                 " modify access granted\n",
739                                 mlist->sml_type, 0, 0 );
740                         continue;
741                 }
742 #endif
743
744                 switch ( mlist->sml_op ) {
745                 case LDAP_MOD_REPLACE:
746                 case LDAP_MOD_ADD:
747                         if ( mlist->sml_bvalues == NULL ) {
748                                 break;
749                         }
750                         for ( i = 0; mlist->sml_bvalues[i] != NULL; i++ ) {
751                                 if ( ! access_allowed( be, conn, op, e,
752                                         mlist->sml_desc, mlist->sml_bvalues[i], ACL_WRITE ) )
753                                 {
754                                         return( 0 );
755                                 }
756                         }
757                         break;
758
759                 case LDAP_MOD_DELETE:
760                         if ( mlist->sml_bvalues == NULL ) {
761                                 if ( ! access_allowed( be, conn, op, e,
762                                         mlist->sml_desc, NULL, ACL_WRITE ) )
763                                 {
764                                         return( 0 );
765                                 }
766                                 break;
767                         }
768                         for ( i = 0; mlist->sml_bvalues[i] != NULL; i++ ) {
769                                 if ( ! access_allowed( be, conn, op, e,
770                                         mlist->sml_desc, mlist->sml_bvalues[i], ACL_WRITE ) )
771                                 {
772                                         return( 0 );
773                                 }
774                         }
775                         break;
776                 }
777         }
778
779         return( 1 );
780 }
781
782 #ifdef SLAPD_ACI_ENABLED
783 static char *
784 aci_bvstrdup( struct berval *bv )
785 {
786         char *s;
787
788         s = (char *)ch_malloc(bv->bv_len + 1);
789         if (s != NULL) {
790                 memcpy(s, bv->bv_val, bv->bv_len);
791                 s[bv->bv_len] = 0;
792         }
793         return(s);
794 }
795
796 static int
797 aci_strbvcmp(
798         const char *s,
799         struct berval *bv )
800 {
801         int res, len;
802
803         res = strncasecmp( s, bv->bv_val, bv->bv_len );
804         if (res)
805                 return(res);
806         len = strlen(s);
807         if (len > (int)bv->bv_len)
808                 return(1);
809         if (len < (int)bv->bv_len)
810                 return(-1);
811         return(0);
812 }
813
814 static int
815 aci_get_part(
816         struct berval *list,
817         int ix,
818         char sep,
819         struct berval *bv )
820 {
821         int len;
822         char *p;
823
824         if (bv) {
825                 bv->bv_len = 0;
826                 bv->bv_val = NULL;
827         }
828         len = list->bv_len;
829         p = list->bv_val;
830         while (len >= 0 && --ix >= 0) {
831                 while (--len >= 0 && *p++ != sep) ;
832         }
833         while (len >= 0 && *p == ' ') {
834                 len--;
835                 p++;
836         }
837         if (len < 0)
838                 return(-1);
839
840         if (!bv)
841                 return(0);
842
843         bv->bv_val = p;
844         while (--len >= 0 && *p != sep) {
845                 bv->bv_len++;
846                 p++;
847         }
848         while (bv->bv_len > 0 && *--p == ' ')
849                 bv->bv_len--;
850         return(bv->bv_len);
851 }
852
853 static int
854 aci_list_map_rights(
855         struct berval *list )
856 {
857         struct berval bv;
858         slap_access_t mask;
859         int i;
860
861         ACL_INIT(mask);
862         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
863                 if (bv.bv_len <= 0)
864                         continue;
865                 switch (*bv.bv_val) {
866                 case 'c':
867                         ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
868                         break;
869                 case 's':
870                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
871                          * the right 's' to mean "set", but in the examples states
872                          * that the right 's' means "search".  The latter definition
873                          * is used here.
874                          */
875                         ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
876                         break;
877                 case 'r':
878                         ACL_PRIV_SET(mask, ACL_PRIV_READ);
879                         break;
880                 case 'w':
881                         ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
882                         break;
883                 case 'x':
884                         /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not 
885                          * define any equivalent to the AUTH right, so I've just used
886                          * 'x' for now.
887                          */
888                         ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
889                         break;
890                 default:
891                         break;
892                 }
893
894         }
895         return(mask);
896 }
897
898 static int
899 aci_list_has_attr(
900         struct berval *list,
901         const char *attr,
902         struct berval *val )
903 {
904         struct berval bv, left, right;
905         int i;
906
907         for (i = 0; aci_get_part(list, i, ',', &bv) >= 0; i++) {
908                 if (aci_get_part(&bv, 0, '=', &left) < 0
909                         || aci_get_part(&bv, 1, '=', &right) < 0)
910                 {
911                         if (aci_strbvcmp(attr, &bv) == 0)
912                                 return(1);
913                 } else if (val == NULL) {
914                         if (aci_strbvcmp(attr, &left) == 0)
915                                 return(1);
916                 } else {
917                         if (aci_strbvcmp(attr, &left) == 0) {
918                                 /* this is experimental code that implements a
919                                  * simple (prefix) match of the attribute value.
920                                  * the ACI draft does not provide for aci's that
921                                  * apply to specific values, but it would be
922                                  * nice to have.  If the <attr> part of an aci's
923                                  * rights list is of the form <attr>=<value>,
924                                  * that means the aci applies only to attrs with
925                                  * the given value.  Furthermore, if the attr is
926                                  * of the form <attr>=<value>*, then <value> is
927                                  * treated as a prefix, and the aci applies to 
928                                  * any value with that prefix.
929                                  *
930                                  * Ideally, this would allow r.e. matches.
931                                  */
932                                 if (aci_get_part(&right, 0, '*', &left) < 0
933                                         || right.bv_len <= left.bv_len)
934                                 {
935                                         if (aci_strbvcmp(val->bv_val, &right) == 0)
936                                                 return(1);
937                                 } else if (val->bv_len >= left.bv_len) {
938                                         if (strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0)
939                                                 return(1);
940                                 }
941                         }
942                 }
943         }
944         return(0);
945 }
946
947 static slap_access_t
948 aci_list_get_attr_rights(
949         struct berval *list,
950         const char *attr,
951         struct berval *val )
952 {
953     struct berval bv;
954     slap_access_t mask;
955     int i;
956
957         /* loop through each rights/attr pair, skip first part (action) */
958         ACL_INIT(mask);
959         for (i = 1; aci_get_part(list, i + 1, ';', &bv) >= 0; i += 2) {
960                 if (aci_list_has_attr(&bv, attr, val) == 0)
961                         continue;
962                 if (aci_get_part(list, i, ';', &bv) < 0)
963                         continue;
964                 mask |= aci_list_map_rights(&bv);
965         }
966         return(mask);
967 }
968
969 static int
970 aci_list_get_rights(
971         struct berval *list,
972         const char *attr,
973         struct berval *val,
974         slap_access_t *grant,
975         slap_access_t *deny )
976 {
977     struct berval perm, actn;
978     slap_access_t *mask;
979     int i, found;
980
981         if (attr == NULL || *attr == 0 || strcasecmp(attr, "entry") == 0) {
982                 attr = "[entry]";
983         }
984
985         found = 0;
986         ACL_INIT(*grant);
987         ACL_INIT(*deny);
988         /* loop through each permissions clause */
989         for (i = 0; aci_get_part(list, i, '$', &perm) >= 0; i++) {
990                 if (aci_get_part(&perm, 0, ';', &actn) < 0)
991                         continue;
992                 if (aci_strbvcmp( "grant", &actn ) == 0) {
993                         mask = grant;
994                 } else if (aci_strbvcmp( "deny", &actn ) == 0) {
995                         mask = deny;
996                 } else {
997                         continue;
998                 }
999
1000                 found = 1;
1001                 *mask |= aci_list_get_attr_rights(&perm, attr, val);
1002                 *mask |= aci_list_get_attr_rights(&perm, "[all]", NULL);
1003         }
1004         return(found);
1005 }
1006
1007 static int
1008 aci_group_member (
1009         struct berval *subj,
1010         const char *defgrpoc,
1011         const char *defgrpat,
1012     Backend             *be,
1013     Entry               *e,
1014     Operation           *op,
1015         regmatch_t      *matches
1016 )
1017 {
1018         struct berval bv;
1019         char *subjdn, *grpdn = NULL;
1020         char *grpoc;
1021         char *grpat;
1022 #ifdef SLAPD_SCHEMA_NOT_COMPAT
1023         ObjectClass *grp_oc = NULL;
1024         AttributeDescription *grp_ad = NULL;
1025         char *text;
1026 #else
1027         char *grp_oc;
1028         char *grp_ad;
1029 #endif
1030         int rc;
1031
1032         /* format of string is "group/objectClassValue/groupAttrName" */
1033         if (aci_get_part(subj, 0, '/', &bv) < 0) {
1034                 return(0);
1035         }
1036
1037         subjdn = aci_bvstrdup(&bv);
1038         if (subjdn == NULL) {
1039                 return(0);
1040         }
1041
1042         if (aci_get_part(subj, 1, '/', &bv) < 0) {
1043                 grpoc = ch_strdup( defgrpoc );
1044         } else {
1045                 grpoc = aci_bvstrdup(&bv);
1046         }
1047
1048         if (aci_get_part(subj, 2, '/', &bv) < 0) {
1049                 grpat = ch_strdup( defgrpat );
1050         } else {
1051                 grpat = aci_bvstrdup(&bv);
1052         }
1053
1054 #ifdef SLAPD_SCHEMA_NOT_COMPAT
1055         rc = slap_str2ad( grpat, &grp_ad, &text );
1056         if( rc != LDAP_SUCCESS ) {
1057                 rc = 0;
1058                 goto done;
1059         }
1060 #else
1061         grp_ad = grpat;
1062 #endif
1063         rc = 0;
1064
1065         grpdn = (char *)ch_malloc(1024);
1066
1067         if (grp_oc != NULL && grp_ad != NULL && grpdn != NULL) {
1068                 string_expand(grpdn, 1024, subjdn, e->e_ndn, matches);
1069                 if ( dn_normalize(grpdn) != NULL ) {
1070                         rc = (backend_group(be, e, grpdn, op->o_ndn, grp_oc, grp_ad) == 0);
1071                 }
1072         }
1073
1074 #ifdef SLAPD_SCHEMA_NOT_COMPAT
1075 done:
1076         if( grp_ad != NULL ) ad_free( grp_ad, 1 );
1077 #endif
1078         ch_free(grpdn);
1079         ch_free(grpat);
1080         ch_free(grpoc);
1081         ch_free(subjdn);
1082         return(rc);
1083 }
1084
1085 static int
1086 aci_mask(
1087     Backend                     *be,
1088     Operation           *op,
1089     Entry                       *e,
1090 #ifdef SLAPD_SCHEMA_NOT_COMPAT
1091         AttributeDescription *desc,
1092 #else
1093     const char          *attr,
1094 #endif
1095     struct berval       *val,
1096     struct berval       *aci,
1097         regmatch_t              *matches,
1098         slap_access_t   *grant,
1099         slap_access_t   *deny
1100 )
1101 {
1102     struct berval bv, perms, sdn;
1103     char *subjdn;
1104         int rc, i;
1105 #ifdef SLAPD_SCHEMA_NOT_COMPAT
1106         char *attr;
1107 #endif
1108
1109         /* parse an aci of the form:
1110                 oid#scope#action;rights;attr;rights;attr$action;rights;attr;rights;attr#dnType#subjectDN
1111
1112            See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
1113            a full description of the format for this attribute.
1114
1115            For now, this routine only supports scope=entry.
1116          */
1117
1118         /* check that the aci has all 5 components */
1119         if (aci_get_part(aci, 4, '#', NULL) < 0)
1120                 return(0);
1121
1122         /* check that the aci family is supported */
1123         if (aci_get_part(aci, 0, '#', &bv) < 0)
1124                 return(0);
1125
1126         /* check that the scope is "entry" */
1127         if (aci_get_part(aci, 1, '#', &bv) < 0
1128                 || aci_strbvcmp( "entry", &bv ) != 0)
1129         {
1130                 return(0);
1131         }
1132
1133         /* get the list of permissions clauses, bail if empty */
1134         if (aci_get_part(aci, 2, '#', &perms) <= 0)
1135                 return(0);
1136
1137         /* check if any permissions allow desired access */
1138         if (aci_list_get_rights(&perms, attr, val, grant, deny) == 0)
1139                 return(0);
1140
1141         /* see if we have a DN match */
1142         if (aci_get_part(aci, 3, '#', &bv) < 0)
1143                 return(0);
1144
1145         if (aci_get_part(aci, 4, '#', &sdn) < 0)
1146                 return(0);
1147
1148         if (aci_strbvcmp( "access-id", &bv ) == 0) {
1149                 subjdn = aci_bvstrdup(&sdn);
1150                 if (subjdn == NULL)
1151                         return(0);
1152                 rc = 1;
1153                 if ( dn_normalize(subjdn) != NULL )
1154                         if (strcasecmp(op->o_ndn, subjdn) != 0)
1155                                 rc = 0;
1156                 ch_free(subjdn);
1157                 return(rc);
1158         }
1159
1160         if (aci_strbvcmp( "self", &bv ) == 0) {
1161                 if (strcasecmp(op->o_ndn, e->e_ndn) == 0)
1162                         return(1);
1163
1164         } else if (aci_strbvcmp( "dnattr", &bv ) == 0) {
1165                 char *dnattr = aci_bvstrdup(&sdn);
1166 #ifdef SLAPD_SCHEMA_NOT_COMPAT
1167                 Attribute *at;
1168                 AttributeDescription *ad = NULL;
1169                 const char *text;
1170
1171                 rc = slap_str2ad( dnattr, &ad, &text );
1172                 ch_free( dnattr );
1173
1174                 if( rc != LDAP_SUCCESS ) {
1175                         return 0;
1176                 }
1177
1178                 rc = 0;
1179
1180                 bv.bv_val = op->o_ndn;
1181                 bv.bv_len = strlen( bv.bv_val );
1182
1183                 for(at = attrs_find( e->e_attrs, ad );
1184                         at != NULL;
1185                         at = attrs_find( at->a_next, ad ) )
1186                 {
1187                         if (value_find( ad, at->a_vals, &bv) == 0 ) {
1188                                 rc = 1;
1189                                 break;
1190                         }
1191                 }
1192
1193                 ad_free( ad, 1 );
1194                 return rc;
1195
1196 #else
1197                 Attribute *at;
1198                 at = attr_find( e->e_attrs, dnattr );
1199                 ch_free( dnattr );
1200
1201                 if (at != NULL) {
1202                         bv.bv_val = op->o_ndn;
1203                         bv.bv_len = strlen( bv.bv_val );
1204
1205                         if (value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0 )
1206                                 return(1);
1207                 }
1208 #endif
1209
1210         } else if (aci_strbvcmp( "group", &bv ) == 0) {
1211                 if (aci_group_member(&sdn, SLAPD_GROUP_CLASS, SLAPD_GROUP_ATTR, be, e, op, matches))
1212                         return(1);
1213
1214         } else if (aci_strbvcmp( "role", &bv ) == 0) {
1215                 if (aci_group_member(&sdn, SLAPD_ROLE_CLASS, SLAPD_ROLE_ATTR, be, e, op, matches))
1216                         return(1);
1217         }
1218
1219         return(0);
1220 }
1221
1222 #endif  /* SLAPD_ACI_ENABLED */
1223
1224 static void
1225 string_expand(
1226         char *newbuf,
1227         int bufsiz,
1228         char *pat,
1229         char *match,
1230         regmatch_t *matches)
1231 {
1232         int     size;
1233         char   *sp;
1234         char   *dp;
1235         int     flag;
1236
1237         size = 0;
1238         newbuf[0] = '\0';
1239         bufsiz--; /* leave space for lone $ */
1240
1241         flag = 0;
1242         for ( dp = newbuf, sp = pat; size < bufsiz && *sp ; sp++) {
1243                 /* did we previously see a $ */
1244                 if (flag) {
1245                         if (*sp == '$') {
1246                                 *dp++ = '$';
1247                                 size++;
1248                         } else if (*sp >= '0' && *sp <= '9' ) {
1249                                 int     n;
1250                                 int     i;
1251                                 int     l;
1252
1253                                 n = *sp - '0';
1254                                 *dp = '\0';
1255                                 i = matches[n].rm_so;
1256                                 l = matches[n].rm_eo; 
1257                                 for ( ; size < 512 && i < l; size++, i++ ) {
1258                                         *dp++ = match[i];
1259                                         size++;
1260                                 }
1261                                 *dp = '\0';
1262                         }
1263                         flag = 0;
1264                 } else {
1265                         if (*sp == '$') {
1266                                 flag = 1;
1267                         } else {
1268                                 *dp++ = *sp;
1269                                 size++;
1270                         }
1271                 }
1272         }
1273
1274         if (flag) {
1275                 /* must have ended with a single $ */
1276                 *dp++ = '$';
1277                 size++;
1278         }
1279
1280         *dp = '\0';
1281
1282         Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %s\n", pat, 0, 0 );
1283         Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", newbuf, 0, 0 );
1284 }
1285
1286 static int
1287 regex_matches(
1288         char *pat,                              /* pattern to expand and match against */
1289         char *str,                              /* string to match against pattern */
1290         char *buf,                              /* buffer with $N expansion variables */
1291         regmatch_t *matches             /* offsets in buffer for $N expansion variables */
1292 )
1293 {
1294         regex_t re;
1295         char newbuf[512];
1296         int     rc;
1297
1298         if(str == NULL) str = "";
1299
1300         string_expand(newbuf, sizeof(newbuf), pat, buf, matches);
1301         if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
1302                 char error[512];
1303                 regerror(rc, &re, error, sizeof(error));
1304
1305                 Debug( LDAP_DEBUG_TRACE,
1306                     "compile( \"%s\", \"%s\") failed %s\n",
1307                         pat, str, error );
1308                 return( 0 );
1309         }
1310
1311         rc = regexec(&re, str, 0, NULL, 0);
1312         regfree( &re );
1313
1314         Debug( LDAP_DEBUG_TRACE,
1315             "=> regex_matches: string:   %s\n", str, 0, 0 );
1316         Debug( LDAP_DEBUG_TRACE,
1317             "=> regex_matches: rc: %d %s\n",
1318                 rc, !rc ? "matches" : "no matches", 0 );
1319         return( !rc );
1320 }
1321