]> git.sur5r.net Git - openldap/blob - servers/slapd/aclparse.c
a1345069089da7475d18e452ab64983a2f2d8650
[openldap] / servers / slapd / aclparse.c
1 /* acl.c - routines to parse and check acl's */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/ctype.h>
13 #include <ac/regex.h>
14 #include <ac/socket.h>
15 #include <ac/string.h>
16 #include <ac/unistd.h>
17
18 #include "slap.h"
19
20 static void             split(char *line, int splitchar, char **left, char **right);
21 static void             acl_append(AccessControl **l, AccessControl *a);
22 static void             access_append(Access **l, Access *a);
23 static void             acl_usage(void) LDAP_GCCATTR((noreturn));
24 #ifdef LDAP_DEBUG
25 static void             print_acl(AccessControl *a);
26 static void             print_access(Access *b);
27 #endif
28
29 static int
30 regtest(const char *fname, int lineno, char *pat) {
31         int e;
32         regex_t re;
33
34         char buf[512];
35         unsigned size;
36
37         char *sp;
38         char *dp;
39         int  flag;
40
41         sp = pat;
42         dp = buf;
43         size = 0;
44         buf[0] = '\0';
45
46         for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
47                 if (flag) {
48                         if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
49                                 *dp++ = *sp;
50                                 size++;
51                         }
52                         flag = 0;
53
54                 } else {
55                         if (*sp == '$') {
56                                 flag = 1;
57                         } else {
58                                 *dp++ = *sp;
59                                 size++;
60                         }
61                 }
62         }
63
64         *dp = '\0';
65         if ( size >= (sizeof(buf)-1) ) {
66                 fprintf( stderr,
67                         "%s: line %d: regular expression \"%s\" too large\n",
68                         fname, lineno, pat );
69                 acl_usage();
70         }
71
72         if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
73                 char error[512];
74                 regerror(e, &re, error, sizeof(error));
75                 fprintf( stderr,
76                         "%s: line %d: regular expression \"%s\" bad because of %s\n",
77                         fname, lineno, pat, error );
78                 acl_usage();
79                 return(0);
80         }
81         regfree(&re);
82         return(1);
83 }
84
85 void
86 parse_acl(
87     Backend     *be,
88     const char  *fname,
89     int         lineno,
90     int         argc,
91     char        **argv
92 )
93 {
94         int             i;
95         char            *left, *right;
96         AccessControl   *a;
97         Access  *b;
98
99         a = NULL;
100         for ( i = 1; i < argc; i++ ) {
101                 /* to clause - select which entries are protected */
102                 if ( strcasecmp( argv[i], "to" ) == 0 ) {
103                         if ( a != NULL ) {
104                                 fprintf( stderr,
105                 "%s: line %d: only one to clause allowed in access line\n",
106                                     fname, lineno );
107                                 acl_usage();
108                         }
109                         a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
110                         a->acl_filter = NULL;
111                         a->acl_dn_pat = NULL;
112                         a->acl_attrs  = NULL;
113                         a->acl_access = NULL;
114                         a->acl_next   = NULL;
115                         for ( ++i; i < argc; i++ ) {
116                                 if ( strcasecmp( argv[i], "by" ) == 0 ) {
117                                         i--;
118                                         break;
119                                 }
120
121                                 if ( strcasecmp( argv[i], "*" ) == 0 ) {
122                                         a->acl_dn_pat = ch_strdup( ".*" );
123                                         continue;
124                                 }
125
126                                 split( argv[i], '=', &left, &right );
127                                 if ( right == NULL || *right == '\0' ) {
128                                         fprintf( stderr,
129         "%s: line %d: missing \"=\" in (or value after) \"%s\" in to clause\n",
130                                             fname, lineno, left );
131                                         acl_usage();
132                                 }
133
134                                 if ( strcasecmp( left, "filter" ) == 0 ) {
135                                         if ( (a->acl_filter = str2filter(
136                                             right )) == NULL ) {
137                                                 fprintf( stderr,
138                                 "%s: line %d: bad filter \"%s\" in to clause\n",
139                                                     fname, lineno, right );
140                                                 acl_usage();
141                                         }
142
143                                 } else if ( strcasecmp( left, "dn" ) == 0 ) {
144                                                 a->acl_dn_pat = ch_strdup( right );
145
146                                 } else if ( strncasecmp( left, "attr", 4 ) == 0 ) {
147                                         char    **alist;
148
149                                         alist = str2charray( right, "," );
150                                         charray_merge( &a->acl_attrs, alist );
151                                         charray_free( alist );
152
153                                 } else {
154                                         fprintf( stderr,
155                                                 "%s: line %d: expecting <what> got \"%s\"\n",
156                                             fname, lineno, left );
157                                         acl_usage();
158                                 }
159                         }
160
161                         if ( a->acl_dn_pat != NULL ) {
162                                 int e = regcomp( &a->acl_dn_re, a->acl_dn_pat,
163                                                  REG_EXTENDED | REG_ICASE );
164                                 if ( e ) {
165                                         char buf[512];
166                                         regerror( e, &a->acl_dn_re, buf, sizeof(buf) );
167                                         fprintf( stderr,
168                                 "%s: line %d: regular expression \"%s\" bad because of %s\n",
169                                                  fname, lineno, right, buf );
170                                         acl_usage();
171                                 }
172                         }
173
174                 /* by clause - select who has what access to entries */
175                 } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
176                         if ( a == NULL ) {
177                                 fprintf( stderr,
178                                         "%s: line %d: to clause required before by clause in access line\n",
179                                     fname, lineno );
180                                 acl_usage();
181                         }
182                         /*
183                          * by clause consists of <who> and <access>
184                          */
185
186                         b = (Access *) ch_calloc( 1, sizeof(Access) );
187
188                         if ( ++i == argc ) {
189                                 fprintf( stderr,
190                             "%s: line %d: premature eol: expecting <who>\n",
191                                     fname, lineno );
192                                 acl_usage();
193                         }
194
195                         /* get <who> */
196                         for ( ; i < argc; i++ ) {
197                                 char *pat;
198                                 split( argv[i], '=', &left, &right );
199
200                                 if ( strcasecmp( argv[i], "*" ) == 0 ) {
201                                         pat = ch_strdup( ".*" );
202                                 } else if ( strcasecmp( argv[i], "anonymous" ) == 0 ) {
203                                         pat = ch_strdup( "anonymous" );
204                                 } else if ( strcasecmp( argv[i], "self" ) == 0 ) {
205                                         pat = ch_strdup( "self" );
206                                 } else if ( strcasecmp( left, "dn" ) == 0 ) {
207                                         regtest(fname, lineno, right);
208                                         pat = ch_strdup( right );
209                                 } else {
210                                         pat = NULL;
211                                 }
212
213                                 if( pat != NULL ) {
214                                         if( b->a_dn_pat != NULL ) {
215                                                 fprintf( stderr,
216                                                     "%s: line %d: dn pattern already specified.\n",
217                                                     fname, lineno );
218                                                 acl_usage();
219                                         }
220
221                                         b->a_dn_pat = pat;
222                                         continue;
223                                 }
224
225                                 if ( strcasecmp( left, "dnattr" ) == 0 ) {
226                                         if( b->a_dn_pat != NULL ) {
227                                                 fprintf( stderr,
228                                                         "%s: line %d: dnaddr already specified.\n",
229                                                         fname, lineno );
230                                                 acl_usage();
231                                         }
232
233                                         b->a_dn_at = ch_strdup( right );
234                                         continue;
235                                 }
236
237                                 if ( strncasecmp( left, "group", sizeof("group")-1 ) == 0 ) {
238                                         char *name = NULL;
239                                         char *value = NULL;
240
241                                         if( b->a_group_pat != NULL ) {
242                                                 fprintf( stderr,
243                                                         "%s: line %d: group pattern already specified.\n",
244                                                         fname, lineno );
245                                                 acl_usage();
246                                         }
247
248                                         /* format of string is "group/objectClassValue/groupAttrName" */
249                                         if ((value = strchr(left, '/')) != NULL) {
250                                                 *value++ = '\0';
251                                                 if (value && *value
252                                                         && (name = strchr(value, '/')) != NULL)
253                                                 {
254                                                         *name++ = '\0';
255                                                 }
256                                         }
257
258                                         regtest(fname, lineno, right);
259                                         b->a_group_pat = ch_strdup( right );
260
261                                         if (value && *value) {
262                                                 b->a_group_oc = ch_strdup(value);
263                                                 *--value = '/';
264                                         } else {
265                                                 b->a_group_oc = ch_strdup("groupOfNames");
266
267                                                 if (name && *name) {
268                                                         b->a_group_at = ch_strdup(name);
269                                                         *--name = '/';
270
271                                                 } else {
272                                                         b->a_group_at = ch_strdup("member");
273                                                 }
274                                         }
275                                         continue;
276                                 }
277
278                                 if ( strcasecmp( left, "peername" ) == 0 ) {
279                                         if( b->a_peername_pat != NULL ) {
280                                                 fprintf( stderr,
281                                                         "%s: line %d: peername pattern already specified.\n",
282                                                         fname, lineno );
283                                                 acl_usage();
284                                         }
285
286                                         regtest(fname, lineno, right);
287                                         b->a_peername_pat = ch_strdup( right );
288                                         continue;
289                                 }
290
291                                 if ( strcasecmp( left, "sockname" ) == 0 ) {
292                                         if( b->a_sockname_pat != NULL ) {
293                                                 fprintf( stderr,
294                                                         "%s: line %d: sockname pattern already specified.\n",
295                                                         fname, lineno );
296                                                 acl_usage();
297                                         }
298
299                                         regtest(fname, lineno, right);
300                                         b->a_sockname_pat = ch_strdup( right );
301                                         continue;
302                                 }
303
304                                 if ( strcasecmp( left, "domain" ) == 0 ) {
305                                         if( b->a_domain_pat != NULL ) {
306                                                 fprintf( stderr,
307                                                         "%s: line %d: domain pattern already specified.\n",
308                                                         fname, lineno );
309                                                 acl_usage();
310                                         }
311
312                                         regtest(fname, lineno, right);
313                                         b->a_domain_pat = ch_strdup( right );
314                                         continue;
315                                 }
316
317                                 if ( strcasecmp( left, "sockurl" ) == 0 ) {
318                                         if( b->a_sockurl_pat != NULL ) {
319                                                 fprintf( stderr,
320                                                         "%s: line %d: sockurl pattern already specified.\n",
321                                                         fname, lineno );
322                                                 acl_usage();
323                                         }
324
325                                         regtest(fname, lineno, right);
326                                         b->a_sockurl_pat = ch_strdup( right );
327                                         continue;
328                                 }
329
330 #ifdef SLAPD_ACI_ENABLED
331                                 if ( strcasecmp( left, "aci" ) == 0 ) {
332                                         if( b->a_aci_at != NULL ) {
333                                                 fprintf( stderr,
334                                                         "%s: line %d: aci attribute already specified.\n",
335                                                         fname, lineno );
336                                                 acl_usage();
337                                         }
338
339                                         if ( right != NULL && *right != '\0' )
340                                                 b->a_aci_at = ch_strdup( right );
341                                         else
342                                                 b->a_aci_at = ch_strdup( SLAPD_ACI_DEFAULT_ATTR );
343                                         continue;
344                                 }
345 #endif
346
347                                 /* get <access> */
348                                 if ( ACL_IS_INVALID(ACL_SET(b->a_access, str2access( left ))) ) {
349                                         fprintf( stderr,
350                                         "%s: line %d: expecting <access> got \"%s\"\n",
351                                                 fname, lineno, left );
352                                         acl_usage();
353                                 }
354                                 access_append( &a->acl_access, b );
355                                 break;
356                         }
357                 } else {
358                         fprintf( stderr,
359                     "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n",
360                             fname, lineno, argv[i] );
361                         acl_usage();
362                 }
363         }
364
365         /* if we have no real access clause, complain and do nothing */
366         if ( a == NULL ) {
367                         fprintf( stderr,
368                                 "%s: line %d: warning: no access clause(s) specified in access line\n",
369                             fname, lineno );
370
371         } else {
372
373 #ifdef LDAP_DEBUG
374                 if (ldap_debug & LDAP_DEBUG_ACL)
375                     print_acl(a);
376 #endif
377         
378                 if ( a->acl_access == NULL ) {
379                         fprintf( stderr,
380                         "%s: line %d: warning: no by clause(s) specified in access line\n",
381                             fname, lineno );
382                 }
383
384                 if ( be != NULL ) {
385                         acl_append( &be->be_acl, a );
386                 } else {
387                         acl_append( &global_acl, a );
388                 }
389         }
390 }
391
392 char *
393 access2str( int access )
394 {
395         static char     buf[12];
396
397         if ( ACL_IS_SELF( access ) ) {
398                 strcpy( buf, "self" );
399         } else {
400                 buf[0] = '\0';
401         }
402
403         if ( ACL_IS_NONE(access) ) {
404                 strcat( buf, "none" );
405         } else if ( ACL_IS_AUTH(access) ) {
406                 strcat( buf, "auth" );
407         } else if ( ACL_IS_COMPARE(access) ) {
408                 strcat( buf, "compare" );
409         } else if ( ACL_IS_SEARCH(access) ) {
410                 strcat( buf, "search" );
411         } else if ( ACL_IS_READ(access) ) {
412                 strcat( buf, "read" );
413         } else if ( ACL_IS_WRITE(access) ) {
414                 strcat( buf, "write" );
415
416         } else {
417                 strcat( buf, "unknown" );
418         }
419
420         return( buf );
421 }
422
423 int
424 str2access( char *str )
425 {
426         int     access;
427
428         ACL_CLR(access);
429
430         if ( strncasecmp( str, "self", 4 ) == 0 ) {
431                 ACL_SET_SELF(access);
432                 str += 4;
433         }
434
435         if ( strcasecmp( str, "none" ) == 0 ) {
436                 ACL_SET_NONE(access);
437         } else if ( strcasecmp( str, "auth" ) == 0 ) {
438                 ACL_SET_AUTH(access);
439         } else if ( strcasecmp( str, "compare" ) == 0 ) {
440                 ACL_SET_COMPARE(access);
441         } else if ( strcasecmp( str, "search" ) == 0 ) {
442                 ACL_SET_SEARCH(access);
443         } else if ( strcasecmp( str, "read" ) == 0 ) {
444                 ACL_SET_READ(access);
445         } else if ( strcasecmp( str, "write" ) == 0 ) {
446                 ACL_SET_WRITE(access);
447         } else {
448                 ACL_SET_INVALID(access);
449         }
450
451         return( access );
452 }
453
454 static void
455 acl_usage( void )
456 {
457         fprintf( stderr, "\n"
458                 "<access clause> ::= access to <what> [ by <who> <access> ]+ \n"
459                 "<what> ::= * | [dn=<regex>] [filter=<ldapfilter>] [attrs=<attrlist>]\n"
460                 "<attrlist> ::= <attr> | <attr> , <attrlist>\n"
461                 "<attr> ::= <attrname> | entry | children\n"
462                 "<who> ::= [ * | anonymous | self | dn=<regex> ]\n"
463                         "\t[dnattr=<attrname>]\n"
464                         "\t[group[/<objectclass>[/<attrname>]]=<regex>]\n"
465                         "\t[peername=<regex>] [sockname=<regex>]\n"
466                         "\t[domain=<regex>] [sockurl=<regex>]\n"
467 #ifdef SLAPD_ACI_ENABLED
468                         "\t[aci=<attrname>]\n"
469 #endif
470                 "<access> ::= [self]{none|auth|compare|search|read|write}\n"
471                 );
472         exit( EXIT_FAILURE );
473 }
474
475 static void
476 split(
477     char        *line,
478     int         splitchar,
479     char        **left,
480     char        **right
481 )
482 {
483         *left = line;
484         if ( (*right = strchr( line, splitchar )) != NULL ) {
485                 *((*right)++) = '\0';
486         }
487 }
488
489 static void
490 access_append( Access **l, Access *a )
491 {
492         for ( ; *l != NULL; l = &(*l)->a_next )
493                 ;       /* NULL */
494
495         *l = a;
496 }
497
498 static void
499 acl_append( AccessControl **l, AccessControl *a )
500 {
501         for ( ; *l != NULL; l = &(*l)->acl_next )
502                 ;       /* NULL */
503
504         *l = a;
505 }
506
507 #ifdef LDAP_DEBUG
508
509 static void
510 print_access( Access *b )
511 {
512         fprintf( stderr, "\tby" );
513
514         if ( b->a_dn_pat != NULL ) {
515                 if( strcmp(b->a_dn_pat, "anonymous") == 0 ) {
516                         fprintf( stderr, " anonymous" );
517
518                 } else if( strcmp(b->a_dn_pat, "self") == 0 ) {
519                         fprintf( stderr, " self" );
520
521                 } else {
522                         fprintf( stderr, " dn=%s", b->a_dn_pat );
523                 }
524         }
525
526         if ( b->a_dn_at != NULL ) {
527                 fprintf( stderr, " dnattr=%s", b->a_dn_at );
528         }
529
530         if ( b->a_group_pat != NULL ) {
531                 fprintf( stderr, " group: %s", b->a_group_pat );
532
533                 if ( b->a_group_oc ) {
534                         fprintf( stderr, " objectClass: %s", b->a_group_oc );
535
536                         if ( b->a_group_at ) {
537                                 fprintf( stderr, " attributeType: %s", b->a_group_at );
538                         }
539                 }
540     }
541
542         if ( b->a_peername_pat != NULL ) {
543                 fprintf( stderr, " peername=%s", b->a_peername_pat );
544         }
545         if ( b->a_sockname_pat != NULL ) {
546                 fprintf( stderr, " sockname=%s", b->a_sockname_pat );
547         }
548
549         if ( b->a_domain_pat != NULL ) {
550                 fprintf( stderr, " domain=%s", b->a_domain_pat );
551         }
552
553         if ( b->a_sockurl_pat != NULL ) {
554                 fprintf( stderr, " sockurl=%s", b->a_sockurl_pat );
555         }
556
557 #ifdef SLAPD_ACI_ENABLED
558         if ( b->a_aci_at != NULL ) {
559                 fprintf( stderr, " aci=%s", b->a_aci_at );
560         }
561 #endif
562
563         fprintf( stderr, "\n" );
564 }
565
566 static void
567 print_acl( AccessControl *a )
568 {
569         int             i;
570         Access  *b;
571
572         if ( a == NULL ) {
573                 fprintf( stderr, "NULL\n" );
574         }
575         fprintf( stderr, "ACL: access to" );
576         if ( a->acl_filter != NULL ) {
577                 fprintf(  stderr," filter=" );
578                 filter_print( a->acl_filter );
579         }
580         if ( a->acl_dn_pat != NULL ) {
581                 fprintf( stderr, " dn=" );
582                 fprintf( stderr, a->acl_dn_pat );
583         }
584         if ( a->acl_attrs != NULL ) {
585                 int     first = 1;
586
587                 fprintf( stderr, "\n attrs=" );
588                 for ( i = 0; a->acl_attrs[i] != NULL; i++ ) {
589                         if ( ! first ) {
590                                 fprintf( stderr, "," );
591                         }
592                         fprintf( stderr, a->acl_attrs[i] );
593                         first = 0;
594                 }
595         }
596         fprintf( stderr, "\n" );
597         for ( b = a->acl_access; b != NULL; b = b->a_next ) {
598                 print_access( b );
599         }
600         fprintf( stderr, "\n" );
601 }
602
603 #endif /* LDAP_DEBUG */