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