]> git.sur5r.net Git - openldap/blob - servers/slapd/acl.c
Cleanup slapd search op deallocation.
[openldap] / servers / slapd / acl.c
1 /* acl.c - routines to parse and check acl's */
2
3 #include "portable.h"
4
5 #include <stdio.h>
6
7 #include <ac/regex.h>
8 #include <ac/socket.h>
9 #include <ac/string.h>
10
11 #include "slap.h"
12
13 static int      regex_matches(char *pat, char *str, char *buf, regmatch_t *matches);
14 static void     string_expand(char *newbuf, int bufsiz, char *pattern,
15                               char *match, regmatch_t *matches);
16
17
18 /*
19  * access_allowed - check whether dn is allowed the requested access
20  * to entry e, attribute attr, value val.  if val is null, access to
21  * the whole attribute is assumed (all values).  this routine finds
22  * the applicable acl and calls acl_access_allowed() to make the
23  * decision.
24  *
25  * returns      0       access NOT allowed
26  *              1       access allowed
27  */
28
29 int
30 access_allowed(
31     Backend             *be,
32     Connection          *conn,
33     Operation           *op,
34     Entry               *e,
35     char                *attr,
36     struct berval       *val,
37     char                *dn,
38     int                 access
39 )
40 {
41         int                             rc;
42         struct acl              *a;
43         char            *edn;
44
45         regmatch_t       matches[MAXREMATCHES];
46         int              i;
47         int              n;
48
49         if ( be == NULL ) {
50                 return( 0 );
51         }
52
53         edn = dn_normalize_case( ch_strdup( e->e_dn ) );
54         Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: entry (%s) attr (%s)\n",
55                 e->e_dn, attr, 0 );
56
57         /* the lastmod attributes are ignored by ACL checking */
58         if ( strcasecmp( attr, "modifiersname" ) == 0 ||
59                 strcasecmp( attr, "modifytimestamp" ) == 0 ||
60                 strcasecmp( attr, "creatorsname" ) == 0 ||
61                 strcasecmp( attr, "createtimestamp" ) == 0 )
62         {
63                 Debug( LDAP_DEBUG_ACL, "LASTMOD attribute: %s access allowed\n",
64                         attr, 0, 0 );
65                 free( edn );
66                 return(1);
67         }
68
69         memset(matches, 0, sizeof(matches));
70
71         a = acl_get_applicable( be, op, e, attr, edn, MAXREMATCHES, matches );
72
73         if (a) {
74                 for (i = 0; i < MAXREMATCHES && matches[i].rm_so > 0; i++) {
75                         Debug( LDAP_DEBUG_ARGS, "=> match[%d]: %d %d ", i,
76                                (int)matches[i].rm_so, (int)matches[i].rm_eo );
77
78                         if( matches[i].rm_so <= matches[0].rm_eo ) {
79                                 for ( n = matches[i].rm_so; n < matches[i].rm_eo; n++) {
80                                         Debug( LDAP_DEBUG_ARGS, "%c", edn[n], 0, 0 );
81                                 }
82                         }
83                         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
84                 }
85         }
86
87         rc = acl_access_allowed( a, be, conn, e, val, op, access, edn, matches );
88         free( edn );
89
90         Debug( LDAP_DEBUG_ACL, "\n=> access_allowed: exit (%s) attr (%s)\n",
91                 e->e_dn, attr, 0);
92
93         return( rc );
94 }
95
96 /*
97  * acl_get_applicable - return the acl applicable to entry e, attribute
98  * attr.  the acl returned is suitable for use in subsequent calls to
99  * acl_access_allowed().
100  */
101
102 struct acl *
103 acl_get_applicable(
104     Backend             *be,
105     Operation           *op,
106     Entry               *e,
107     char                *attr,
108     char                *edn,
109     int                 nmatch,
110     regmatch_t  *matches
111 )
112 {
113         int             i, j;
114         struct acl      *a;
115
116         Debug( LDAP_DEBUG_ACL, "\n=> acl_get: entry (%s) attr (%s)\n",
117                 e->e_dn, attr, 0 );
118
119         if ( be_isroot( be, op->o_dn ) ) {
120                 Debug( LDAP_DEBUG_ACL,
121                     "<= acl_get: no acl applicable to database root\n", 0, 0,
122                     0 );
123                 return( NULL );
124         }
125
126         Debug( LDAP_DEBUG_ARGS, "=> acl_get: edn %s\n", edn, 0, 0 );
127
128         /* check for a backend-specific acl that matches the entry */
129         for ( i = 1, a = be->be_acl; a != NULL; a = a->acl_next, i++ ) {
130                 if (a->acl_dnpat != NULL) {
131                         Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", 
132                                 i, a->acl_dnpat, (int) a->acl_dnre.re_nsub);
133
134                         if (regexec(&a->acl_dnre, edn, nmatch, matches, 0))
135                                 continue;
136                         else
137                                 Debug( LDAP_DEBUG_TRACE, "=> acl_get:[%d]  backend ACL match\n",
138                                         i, 0, 0);
139                 }
140
141                 if ( a->acl_filter != NULL ) {
142                         if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
143                                 continue;
144                         }
145                 }
146
147         Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr %s\n", i, attr, 0);
148
149                 if ( attr == NULL || a->acl_attrs == NULL ||
150                         charray_inlist( a->acl_attrs, attr ) )
151                 {
152                         Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] backend acl %s attr: %s\n",
153                                 i, e->e_dn, attr );
154                         return( a );
155                 }
156                 matches[0].rm_so = matches[0].rm_eo = -1;
157         }
158
159         /* check for a global acl that matches the entry */
160         for ( i = 1, a = global_acl; a != NULL; a = a->acl_next, i++ ) {
161                 if (a->acl_dnpat != NULL) {
162                         Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", 
163                                 i, a->acl_dnpat, (int) a->acl_dnre.re_nsub);
164
165                         if (regexec(&a->acl_dnre, edn, nmatch, matches, 0)) {
166                                 continue;
167                         } else {
168                                 Debug( LDAP_DEBUG_TRACE, "=> acl_get: [%d] global ACL match\n",
169                                         i, 0, 0);
170                         }
171                 }
172
173                 if ( a->acl_filter != NULL ) {
174                         if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
175                                 continue;
176                         }
177                 }
178
179                 Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr\n", i, 0, 0);
180
181                 if ( attr == NULL || a->acl_attrs == NULL ||
182                         charray_inlist( a->acl_attrs, attr ) )
183                 {
184                         Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] global acl %s attr: %s\n",
185                                 i, e->e_dn, attr );
186                         return( a );
187                 }
188
189                 matches[0].rm_so = matches[0].rm_eo = -1;
190         }
191
192         Debug( LDAP_DEBUG_ACL, "<= acl_get: no match\n", 0, 0, 0 );
193         return( NULL );
194 }
195
196 /*
197  * acl_access_allowed - check whether the given acl allows dn the
198  * requested access to entry e, attribute attr, value val.  if val
199  * is null, access to the whole attribute is assumed (all values).
200  *
201  * returns      0       access NOT allowed
202  *              1       access allowed
203  */
204
205 int
206 acl_access_allowed(
207     struct acl          *a,
208     Backend             *be,
209     Connection          *conn,
210     Entry               *e,
211     struct berval       *val,
212     Operation           *op,
213     int                 access,
214         char            *edn,
215         regmatch_t      *matches
216 )
217 {
218         int             i;
219         char            *odn;
220         struct access   *b;
221         Attribute       *at;
222         struct berval   bv;
223         int             default_access;
224
225         Debug( LDAP_DEBUG_ACL,
226                 "\n=> acl_access_allowed: %s access to entry \"%s\"\n",
227                 access2str( access ), e->e_dn, 0 );
228
229         Debug( LDAP_DEBUG_ACL,
230                 "\n=> acl_access_allowed: %s access to value \"%s\" by \"%s\"\n",
231             access2str( access ),
232                 val ? val->bv_val : "any",
233                 op->o_dn ?  op->o_dn : "" );
234
235         if ( be_isroot( be, op->o_dn ) ) {
236                 Debug( LDAP_DEBUG_ACL,
237                         "<= acl_access_allowed: granted to database root\n",
238                     0, 0, 0 );
239                 return( 1 );
240         }
241
242         default_access = be->be_dfltaccess ? be->be_dfltaccess : global_default_access;
243
244         if ( a == NULL ) {
245                 Debug( LDAP_DEBUG_ACL,
246                     "<= acl_access_allowed: %s by default (no matching to)\n",
247                     default_access >= access ? "granted" : "denied", 0, 0 );
248                 return( default_access >= access );
249         }
250
251         odn = NULL;
252         if ( op->o_dn != NULL ) {
253                 odn = dn_normalize_case( ch_strdup( op->o_dn ) );
254                 bv.bv_val = odn;
255                 bv.bv_len = strlen( odn );
256         }
257         for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
258                 if ( b->a_dnpat != NULL ) {
259                         Debug( LDAP_DEBUG_TRACE, "<= check a_dnpat: %s\n",
260                                 b->a_dnpat, 0, 0);
261                         /*
262                          * if access applies to the entry itself, and the
263                          * user is bound as somebody in the same namespace as
264                          * the entry, OR the given dn matches the dn pattern
265                          */
266                         if ( strcasecmp( b->a_dnpat, "self" ) == 0 && 
267                                 op->o_dn != NULL && *(op->o_dn) && e->e_dn != NULL ) 
268                         {
269                                 if ( strcasecmp( edn, op->o_dn ) == 0 ) {
270                                         Debug( LDAP_DEBUG_ACL,
271                                         "<= acl_access_allowed: matched by clause #%d access %s\n",
272                                             i, (b->a_access & ~ACL_SELF) >=
273                                             access ? "granted" : "denied", 0 );
274
275                                         if ( odn ) free( odn );
276                                         return( (b->a_access & ~ACL_SELF) >= access );
277                                 }
278                         } else {
279                                 if ( regex_matches( b->a_dnpat, odn, edn, matches ) ) {
280                                         Debug( LDAP_DEBUG_ACL,
281                                     "<= acl_access_allowed: matched by clause #%d access %s\n",
282                                     i, (b->a_access & ~ACL_SELF) >= access ?
283                                             "granted" : "denied", 0 );
284
285                                         if ( odn ) free( odn );
286                                         return( (b->a_access & ~ACL_SELF) >= access );
287                                 }
288                         }
289                 }
290                 if ( b->a_addrpat != NULL ) {
291                         if ( regex_matches( b->a_addrpat, conn->c_addr, edn, matches ) ) {
292                                 Debug( LDAP_DEBUG_ACL,
293                                     "<= acl_access_allowed: matched by clause #%d access %s\n",
294                                     i, (b->a_access & ~ACL_SELF) >= access ?
295                                     "granted" : "denied", 0 );
296
297                                 if ( odn ) free( odn );
298                                 return( (b->a_access & ~ACL_SELF) >= access );
299                         }
300                 }
301                 if ( b->a_domainpat != NULL ) {
302                         Debug( LDAP_DEBUG_ARGS, "<= check a_domainpath: %s\n",
303                                 b->a_domainpat, 0, 0 );
304                         if ( regex_matches( b->a_domainpat, conn->c_domain, edn, matches ) ) 
305                         {
306                                 Debug( LDAP_DEBUG_ACL,
307                                     "<= acl_access_allowed: matched by clause #%d access %s\n",
308                                     i, (b->a_access & ~ACL_SELF) >= access ?
309                                     "granted" : "denied", 0 );
310
311                                 if ( odn ) free( odn );
312                                 return( (b->a_access & ~ACL_SELF) >= access );
313                         }
314                 }
315                 if ( b->a_dnattr != NULL && op->o_dn != NULL ) {
316                         Debug( LDAP_DEBUG_ARGS, "<= check a_dnattr: %s\n",
317                                 b->a_dnattr, 0, 0);
318                         /* see if asker is listed in dnattr */
319                         if ( (at = attr_find( e->e_attrs, b->a_dnattr )) != NULL && 
320                                 value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0 )
321                         {
322                                 if ( (b->a_access & ACL_SELF) && 
323                                         (val == NULL || value_cmp( &bv, val, at->a_syntax, 2 )) )
324                                 {
325                                         continue;
326                                 }
327
328                                 if ( odn ) free( odn );
329                                 Debug( LDAP_DEBUG_ACL,
330                                     "<= acl_acces_allowed: matched by clause #%d access %s\n",
331                                     i, (b->a_access & ~ACL_SELF) >= access ?
332                                     "granted" : "denied", 0 );
333
334                                 return( (b->a_access & ~ACL_SELF) >= access );
335                         }
336
337                         /* asker not listed in dnattr - check for self access */
338                         if ( ! (b->a_access & ACL_SELF) || val == NULL ||
339                                 value_cmp( &bv, val, at->a_syntax, 2 ) != 0 )
340                         {
341                                 continue;
342                         }
343
344                         if ( odn ) free( odn );
345                         Debug( LDAP_DEBUG_ACL,
346                                 "<= acl_access_allowed: matched by clause #%d (self) access %s\n",
347                             i, (b->a_access & ~ACL_SELF) >= access ? "granted"
348                             : "denied", 0 );
349
350                         return( (b->a_access & ~ACL_SELF) >= access );
351                 }
352 #ifdef SLAPD_ACLGROUPS
353                 if ( b->a_group != NULL && op->o_dn != NULL ) {
354                         char buf[512];
355
356                         /* b->a_group is an unexpanded entry name, expanded it should be an 
357                          * entry with objectclass group* and we test to see if odn is one of
358                          * the values in the attribute group
359                          */
360                         /* see if asker is listed in dnattr */
361                         string_expand(buf, sizeof(buf), b->a_group, edn, matches);
362
363                         if (be_group(be, e, buf, odn,
364                                 b->a_objectclassvalue, b->a_groupattrname) == 0)
365                         {
366                                 Debug( LDAP_DEBUG_ACL,
367                                         "<= acl_access_allowed: matched by clause #%d (group) access granted\n",
368                                         i, 0, 0 );
369                                 if ( odn ) free( odn );
370                                 return( (b->a_access & ~ACL_SELF) >= access );
371                         }
372                 }
373 #endif /* SLAPD_ACLGROUPS */
374         }
375
376         if ( odn ) free( odn );
377         Debug( LDAP_DEBUG_ACL,
378                 "<= acl_access_allowed: %s by default (no matching by)\n",
379             default_access >= access ? "granted" : "denied", 0, 0 );
380
381         return( default_access >= access );
382 }
383
384 /*
385  * acl_check_modlist - check access control on the given entry to see if
386  * it allows the given modifications by the user associated with op.
387  * returns      LDAP_SUCCESS    mods allowed ok
388  *              anything else   mods not allowed - return is an error
389  *                              code indicating the problem
390  */
391
392 int
393 acl_check_modlist(
394     Backend     *be,
395     Connection  *conn,
396     Operation   *op,
397     Entry       *e,
398     LDAPModList *mlist
399 )
400 {
401         int             i;
402         struct acl      *a;
403         char            *edn;
404
405         edn = dn_normalize_case( ch_strdup( e->e_dn ) );
406
407         for ( ; mlist != NULL; mlist = mlist->ml_next ) {
408                 regmatch_t       matches[MAXREMATCHES];
409
410                 /* the lastmod attributes are ignored by ACL checking */
411                 if ( strcasecmp( mlist->ml_type, "modifiersname" ) == 0 ||
412                         strcasecmp( mlist->ml_type, "modifytimestamp" ) == 0 ||
413                         strcasecmp( mlist->ml_type, "creatorsname" ) == 0 ||
414                         strcasecmp( mlist->ml_type, "createtimestamp" ) == 0 ) 
415                 {
416                         Debug( LDAP_DEBUG_ACL, "LASTMOD attribute: %s access allowed\n",
417                                 mlist->ml_type, 0, 0 );
418                         continue;
419                 }
420
421                 a = acl_get_applicable( be, op, e, mlist->ml_type, edn,
422                         MAXREMATCHES, matches );
423
424                 switch ( mlist->ml_op & ~LDAP_MOD_BVALUES ) {
425                 case LDAP_MOD_REPLACE:
426                 case LDAP_MOD_ADD:
427                         if ( mlist->ml_bvalues == NULL ) {
428                                 break;
429                         }
430                         for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) {
431                                 if ( ! acl_access_allowed( a, be, conn, e, mlist->ml_bvalues[i], 
432                                         op, ACL_WRITE, edn, matches) ) 
433                                 {
434                                         free(edn);
435                                         return( LDAP_INSUFFICIENT_ACCESS );
436                                 }
437                         }
438                         break;
439
440                 case LDAP_MOD_DELETE:
441                         if ( mlist->ml_bvalues == NULL ) {
442                                 if ( ! acl_access_allowed( a, be, conn, e,
443                                         NULL, op, ACL_WRITE, edn, matches) ) 
444                                 {
445                                         free(edn);
446                                         return( LDAP_INSUFFICIENT_ACCESS );
447                                 }
448                                 break;
449                         }
450                         for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) {
451                                 if ( ! acl_access_allowed( a, be, conn, e, mlist->ml_bvalues[i], 
452                                         op, ACL_WRITE, edn, matches) ) 
453                                 {
454                                         free(edn);
455                                         return( LDAP_INSUFFICIENT_ACCESS );
456                                 }
457                         }
458                         break;
459                 }
460         }
461
462         free(edn);
463         return( LDAP_SUCCESS );
464 }
465
466 static void
467 string_expand(
468         char *newbuf,
469         int bufsiz,
470         char *pat,
471         char *match,
472         regmatch_t *matches)
473 {
474         int     size;
475         char   *sp;
476         char   *dp;
477         int     flag;
478
479         size = 0;
480         newbuf[0] = '\0';
481
482         flag = 0;
483         for ( dp = newbuf, sp = pat; size < bufsiz && *sp ; sp++) {
484                 /* did we previously see a $ */
485                 if (flag) {
486                         if (*sp == '$') {
487                                 *dp++ = '$';
488                                 size++;
489                         } else if (*sp >= '0' && *sp <= '9' ) {
490                                 int     n;
491                                 int     i;
492                                 char   *ep;
493                                 int     l;
494
495                                 n = *sp - '0';
496                                 *dp = '\0';
497                                 i = matches[n].rm_so;
498                                 l = matches[n].rm_eo; 
499                                 for ( ; size < 512 && i < l; size++, i++ ) {
500                                         *dp++ = match[i];
501                                         size++;
502                                 }
503                                 *dp = '\0';
504                         }
505                         flag = 0;
506                 } else {
507                         if (*sp == '$') {
508                                 flag = 1;
509                         } else {
510                                 *dp++ = *sp;
511                                 size++;
512                         }
513                 }
514         }
515         *dp = '\0';
516
517         Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %s\n", pat, 0, 0 );
518         Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", newbuf, 0, 0 );
519 }
520
521 static int
522 regex_matches(
523         char *pat,                              /* pattern to expand and match against */
524         char *str,                              /* string to match against pattern */
525         char *buf,                              /* buffer with $N expansion variables */
526         regmatch_t *matches             /* offsets in buffer for $N expansion variables */
527 )
528 {
529         regex_t re;
530         char newbuf[512];
531         int     rc;
532
533         string_expand(newbuf, sizeof(newbuf), pat, buf, matches);
534         if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
535                 char error[512];
536                 regerror(rc, &re, error, sizeof(error));
537
538                 Debug( LDAP_DEBUG_TRACE,
539                     "compile( \"%s\", \"%s\") failed %s\n",
540                         pat, str, error );
541                 return( 0 );
542         }
543
544         rc = regexec(&re, str, 0, NULL, 0);
545         regfree( &re );
546
547         Debug( LDAP_DEBUG_TRACE,
548             "=> regex_matches: string:   %s\n", str, 0, 0 );
549         Debug( LDAP_DEBUG_TRACE,
550             "=> regex_matches: rc: %d %s\n",
551                 rc, !rc ? "matches" : "no matches", 0 );
552         return( !rc );
553 }
554