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