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