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