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