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