]> git.sur5r.net Git - openldap/blob - servers/slapd/acl.c
When recreating a database from an ldif file created by ldbmcat,
[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         AccessControl   *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_no_usermod_attr( 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 AccessControl *
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         AccessControl   *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_dn_pat != NULL) {
127                         Debug( LDAP_DEBUG_TRACE, "=> dnpat: [%d] %s nsub: %d\n", 
128                                 i, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub);
129
130                         if (regexec(&a->acl_dn_re, edn, nmatch, matches, 0)) {
131                                 continue;
132
133                         } else {
134                                 Debug( LDAP_DEBUG_TRACE, "=> acl_get:[%d]  backend ACL match\n",
135                                         i, 0, 0);
136                         }
137                 }
138
139                 if ( a->acl_filter != NULL ) {
140                         if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
141                                 continue;
142                         }
143                 }
144
145         Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr %s\n", i, attr, 0);
146
147                 if ( attr == NULL || a->acl_attrs == NULL ||
148                         charray_inlist( a->acl_attrs, attr ) )
149                 {
150                         Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] backend acl %s attr: %s\n",
151                                 i, e->e_dn, attr );
152                         return( a );
153                 }
154                 matches[0].rm_so = matches[0].rm_eo = -1;
155         }
156
157         /* check for a global acl that matches the entry */
158         for ( i = 1, a = global_acl; a != NULL; a = a->acl_next, i++ ) {
159                 if (a->acl_dn_pat != NULL) {
160                         Debug( LDAP_DEBUG_TRACE, "=> dn pat: [%d] %s nsub: %d\n", 
161                                 i, a->acl_dn_pat, (int) a->acl_dn_re.re_nsub);
162
163                         if (regexec(&a->acl_dn_re, edn, nmatch, matches, 0)) {
164                                 continue;
165
166                         } else {
167                                 Debug( LDAP_DEBUG_TRACE, "=> acl_get: [%d] global ACL match\n",
168                                         i, 0, 0);
169                         }
170                 }
171
172                 if ( a->acl_filter != NULL ) {
173                         if ( test_filter( NULL, NULL, NULL, e, a->acl_filter ) != 0 ) {
174                                 continue;
175                         }
176                 }
177
178                 Debug( LDAP_DEBUG_ARGS, "=> acl_get: [%d] check attr\n", i, 0, 0);
179
180                 if ( attr == NULL || a->acl_attrs == NULL ||
181                         charray_inlist( a->acl_attrs, attr ) )
182                 {
183                         Debug( LDAP_DEBUG_ACL, "<= acl_get: [%d] global acl %s attr: %s\n",
184                                 i, e->e_dn, attr );
185                         return( a );
186                 }
187
188                 matches[0].rm_so = matches[0].rm_eo = -1;
189         }
190
191         Debug( LDAP_DEBUG_ACL, "<= acl_get: no match\n", 0, 0, 0 );
192         return( NULL );
193 }
194
195 /*
196  * acl_access_allowed - check whether the given acl allows dn the
197  * requested access to entry e, attribute attr, value val.  if val
198  * is null, access to the whole attribute is assumed (all values).
199  *
200  * returns      0       access NOT allowed
201  *              1       access allowed
202  */
203
204 int
205 acl_access_allowed(
206     AccessControl       *a,
207     Backend             *be,
208     Connection          *conn,
209     Entry               *e,
210     struct berval       *val,
211     Operation           *op,
212     int                 access,
213         char            *edn,
214         regmatch_t      *matches
215 )
216 {
217         int             i;
218         Access  *b;
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         for ( i = 1, b = a->acl_access; b != NULL; b = b->a_next, i++ ) {
248                 /* AND <who> clauses */
249                 if ( b->a_dn_pat != NULL ) {
250                         Debug( LDAP_DEBUG_TRACE, "<= check a_dn_pat: %s\n",
251                                 b->a_dn_pat, 0, 0);
252                         /*
253                          * if access applies to the entry itself, and the
254                          * user is bound as somebody in the same namespace as
255                          * the entry, OR the given dn matches the dn pattern
256                          */
257                         if ( strcasecmp( b->a_dn_pat, "anonymous" ) == 0 ) {
258                                 if (op->o_ndn != NULL && op->o_ndn[0] != '\0' ) {
259                                         continue;
260                                 }
261
262                         } else if ( strcasecmp( b->a_dn_pat, "self" ) == 0 ) {
263                                 if( op->o_ndn == NULL || op->o_ndn[0] == '\0' ) {
264                                         continue;
265                                 }
266                                 
267                                 if ( e->e_dn == NULL || strcmp( edn, op->o_ndn ) != 0 ) {
268                                         continue;
269                                 }
270
271                         } else if ( strcmp( b->a_dn_pat, ".*" ) != 0 &&
272                                 !regex_matches( b->a_dn_pat, op->o_ndn, edn, matches ) )
273                         {
274                                 continue;
275                         }
276                 }
277
278                 if ( b->a_sockurl_pat != NULL ) {
279                         Debug( LDAP_DEBUG_ARGS, "<= check a_sockurl_pat: %s\n",
280                                 b->a_sockurl_pat, 0, 0 );
281
282                         if ( strcmp( b->a_sockurl_pat, ".*" ) != 0 &&
283                                 !regex_matches( b->a_sockurl_pat, conn->c_listener_url,
284                                 edn, matches ) ) 
285                         {
286                                 continue;
287                         }
288                 }
289
290                 if ( b->a_domain_pat != NULL ) {
291                         Debug( LDAP_DEBUG_ARGS, "<= check a_domain_pat: %s\n",
292                                 b->a_domain_pat, 0, 0 );
293
294                         if ( strcmp( b->a_domain_pat, ".*" ) != 0 &&
295                                 !regex_matches( b->a_domain_pat, conn->c_peer_domain,
296                                 edn, matches ) ) 
297                         {
298                                 continue;
299                         }
300                 }
301
302                 if ( b->a_peername_pat != NULL ) {
303                         Debug( LDAP_DEBUG_ARGS, "<= check a_peername_path: %s\n",
304                                 b->a_peername_pat, 0, 0 );
305
306                         if ( strcmp( b->a_peername_pat, ".*" ) != 0 &&
307                                 !regex_matches( b->a_peername_pat, conn->c_peer_name,
308                                 edn, matches ) )
309                         {
310                                 continue;
311                         }
312                 }
313
314                 if ( b->a_sockname_pat != NULL ) {
315                         Debug( LDAP_DEBUG_ARGS, "<= check a_sockname_path: %s\n",
316                                 b->a_sockname_pat, 0, 0 );
317
318                         if ( strcmp( b->a_sockname_pat, ".*" ) != 0 &&
319                                 !regex_matches( b->a_sockname_pat, conn->c_sock_name,
320                                 edn, matches ) )
321                         {
322                                 continue;
323                         }
324                 }
325
326                 if ( b->a_dn_at != NULL && op->o_ndn != NULL ) {
327                         Attribute       *at;
328                         struct berval   bv;
329
330                         Debug( LDAP_DEBUG_ARGS, "<= check a_dn_at: %s\n",
331                                 b->a_dn_at, 0, 0);
332
333                         bv.bv_val = op->o_ndn;
334                         bv.bv_len = strlen( bv.bv_val );
335
336                         /* see if asker is listed in dnattr */ 
337                         if ( (at = attr_find( e->e_attrs, b->a_dn_at )) != NULL &&
338                                 value_find( at->a_vals, &bv, at->a_syntax, 3 ) == 0 )
339                         {
340                                 if ( ACL_IS_SELF(b->a_access) && 
341                                         (val == NULL || value_cmp( &bv, val, at->a_syntax, 2 )) )
342                                 {
343                                         continue;
344                                 }
345
346                         /* asker not listed in dnattr - check for self access */
347                         } else if ( ! ACL_IS_SELF(b->a_access) || val == NULL ||
348                                 value_cmp( &bv, val, at->a_syntax, 2 ) != 0 )
349                         {
350                                 continue;
351                         }
352                 }
353
354                 if ( b->a_group_pat != NULL && op->o_ndn != NULL ) {
355                         char buf[1024];
356
357                         /* b->a_group is an unexpanded entry name, expanded it should be an 
358                          * entry with objectclass group* and we test to see if odn is one of
359                          * the values in the attribute group
360                          */
361                         /* see if asker is listed in dnattr */
362                         string_expand(buf, sizeof(buf), b->a_group_pat, edn, matches);
363                         if ( dn_normalize_case(buf) == NULL ) {
364                                 /* did not expand to a valid dn */
365                                 continue;
366                         }
367
368                         if (backend_group(be, e, buf, op->o_ndn,
369                                 b->a_group_oc, b->a_group_at) != 0)
370                         {
371                                 continue;
372                         }
373                 }
374
375                 Debug( LDAP_DEBUG_ACL,
376                         "<= acl_access_allowed: matched by clause #%d access %s\n",
377                         i,
378                         ACL_GRANT(b->a_access, access) ? "granted" : "denied",
379                         0 );
380
381                 return ACL_GRANT(b->a_access, access );
382         }
383
384         Debug( LDAP_DEBUG_ACL,
385                 "<= acl_access_allowed: %s by default (no matching by)\n",
386             default_access >= access ? "granted" : "denied", 0, 0 );
387
388         return( default_access >= access );
389 }
390
391 /*
392  * acl_check_modlist - check access control on the given entry to see if
393  * it allows the given modifications by the user associated with op.
394  * returns      LDAP_SUCCESS    mods allowed ok
395  *              anything else   mods not allowed - return is an error
396  *                              code indicating the problem
397  */
398
399 int
400 acl_check_modlist(
401     Backend     *be,
402     Connection  *conn,
403     Operation   *op,
404     Entry       *e,
405     LDAPModList *mlist
406 )
407 {
408         int             i;
409         AccessControl   *a;
410         char    *edn = e->e_ndn;
411
412         for ( ; mlist != NULL; mlist = mlist->ml_next ) {
413                 regmatch_t       matches[MAXREMATCHES];
414
415                 /* the lastmod attributes are ignored by ACL checking */
416                 if ( oc_check_no_usermod_attr( mlist->ml_type ) ) {
417                         Debug( LDAP_DEBUG_ACL, "Operational attribute: %s access allowed\n",
418                                 mlist->ml_type, 0, 0 );
419                         continue;
420                 }
421
422                 a = acl_get_applicable( be, op, e, mlist->ml_type,
423                         MAXREMATCHES, matches );
424
425                 switch ( mlist->ml_op & ~LDAP_MOD_BVALUES ) {
426                 case LDAP_MOD_REPLACE:
427                 case LDAP_MOD_ADD:
428                         if ( mlist->ml_bvalues == NULL ) {
429                                 break;
430                         }
431                         for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) {
432                                 if ( ! acl_access_allowed( a, be, conn, e, mlist->ml_bvalues[i], 
433                                         op, ACL_WRITE, edn, matches) ) 
434                                 {
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                                         return( LDAP_INSUFFICIENT_ACCESS );
446                                 }
447                                 break;
448                         }
449                         for ( i = 0; mlist->ml_bvalues[i] != NULL; i++ ) {
450                                 if ( ! acl_access_allowed( a, be, conn, e, mlist->ml_bvalues[i], 
451                                         op, ACL_WRITE, edn, matches) ) 
452                                 {
453                                         return( LDAP_INSUFFICIENT_ACCESS );
454                                 }
455                         }
456                         break;
457                 }
458         }
459
460         return( LDAP_SUCCESS );
461 }
462
463 static void
464 string_expand(
465         char *newbuf,
466         int bufsiz,
467         char *pat,
468         char *match,
469         regmatch_t *matches)
470 {
471         int     size;
472         char   *sp;
473         char   *dp;
474         int     flag;
475
476         size = 0;
477         newbuf[0] = '\0';
478
479         flag = 0;
480         for ( dp = newbuf, sp = pat; size < bufsiz && *sp ; sp++) {
481                 /* did we previously see a $ */
482                 if (flag) {
483                         if (*sp == '$') {
484                                 *dp++ = '$';
485                                 size++;
486                         } else if (*sp >= '0' && *sp <= '9' ) {
487                                 int     n;
488                                 int     i;
489                                 int     l;
490
491                                 n = *sp - '0';
492                                 *dp = '\0';
493                                 i = matches[n].rm_so;
494                                 l = matches[n].rm_eo; 
495                                 for ( ; size < 512 && i < l; size++, i++ ) {
496                                         *dp++ = match[i];
497                                         size++;
498                                 }
499                                 *dp = '\0';
500                         }
501                         flag = 0;
502                 } else {
503                         if (*sp == '$') {
504                                 flag = 1;
505                         } else {
506                                 *dp++ = *sp;
507                                 size++;
508                         }
509                 }
510         }
511         *dp = '\0';
512
513         Debug( LDAP_DEBUG_TRACE, "=> string_expand: pattern:  %s\n", pat, 0, 0 );
514         Debug( LDAP_DEBUG_TRACE, "=> string_expand: expanded: %s\n", newbuf, 0, 0 );
515 }
516
517 static int
518 regex_matches(
519         char *pat,                              /* pattern to expand and match against */
520         char *str,                              /* string to match against pattern */
521         char *buf,                              /* buffer with $N expansion variables */
522         regmatch_t *matches             /* offsets in buffer for $N expansion variables */
523 )
524 {
525         regex_t re;
526         char newbuf[512];
527         int     rc;
528
529         if(str == NULL) str = "";
530
531         string_expand(newbuf, sizeof(newbuf), pat, buf, matches);
532         if (( rc = regcomp(&re, newbuf, REG_EXTENDED|REG_ICASE))) {
533                 char error[512];
534                 regerror(rc, &re, error, sizeof(error));
535
536                 Debug( LDAP_DEBUG_TRACE,
537                     "compile( \"%s\", \"%s\") failed %s\n",
538                         pat, str, error );
539                 return( 0 );
540         }
541
542         rc = regexec(&re, str, 0, NULL, 0);
543         regfree( &re );
544
545         Debug( LDAP_DEBUG_TRACE,
546             "=> regex_matches: string:   %s\n", str, 0, 0 );
547         Debug( LDAP_DEBUG_TRACE,
548             "=> regex_matches: rc: %d %s\n",
549                 rc, !rc ? "matches" : "no matches", 0 );
550         return( !rc );
551 }
552