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