]> git.sur5r.net Git - openldap/blob - servers/slapd/aclparse.c
a2d585a7c749684cae6c3b54f50aee48a2853193
[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 #ifdef SLAPD_ACI_ENABLED
325                                 if ( strcasecmp( left, "aci" ) == 0 ) {
326                                         if( b->a_aci_at != NULL ) {
327                                                 fprintf( stderr,
328                                                         "%s: line %d: aci attribute already specified.\n",
329                                                         fname, lineno );
330                                                 acl_usage();
331                                         }
332
333                                         if ( right != NULL && *right != '\0' )
334                                                 b->a_aci_at = ch_strdup( right );
335                                         else
336                                                 b->a_aci_at = ch_strdup( SLAPD_ACI_DEFAULT_ATTR );
337                                         continue;
338                                 }
339 #endif
340
341                                 /* get <access> */
342                                 if ( ACL_IS_INVALID(ACL_SET(b->a_access, str2access( left ))) ) {
343                                         fprintf( stderr,
344                                         "%s: line %d: expecting <access> got \"%s\"\n",
345                                                 fname, lineno, left );
346                                         acl_usage();
347                                 }
348                                 access_append( &a->acl_access, b );
349                                 break;
350                         }
351                 } else {
352                         fprintf( stderr,
353                     "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n",
354                             fname, lineno, argv[i] );
355                         acl_usage();
356                 }
357         }
358
359         /* if we have no real access clause, complain and do nothing */
360         if ( a == NULL ) {
361                         fprintf( stderr,
362                                 "%s: line %d: warning: no access clause(s) specified in access line\n",
363                             fname, lineno );
364
365         } else {
366
367 #ifdef LDAP_DEBUG
368                 if (ldap_debug & LDAP_DEBUG_ACL)
369                     print_acl(a);
370 #endif
371         
372                 if ( a->acl_access == NULL ) {
373                         fprintf( stderr,
374                         "%s: line %d: warning: no by clause(s) specified in access line\n",
375                             fname, lineno );
376                 }
377
378                 if ( be != NULL ) {
379                         acl_append( &be->be_acl, a );
380                 } else {
381                         acl_append( &global_acl, a );
382                 }
383         }
384 }
385
386 char *
387 access2str( int access )
388 {
389         static char     buf[12];
390
391         if ( ACL_IS_SELF( access ) ) {
392                 strcpy( buf, "self" );
393         } else {
394                 buf[0] = '\0';
395         }
396
397         if ( ACL_IS_NONE(access) ) {
398                 strcat( buf, "none" );
399         } else if ( ACL_IS_AUTH(access) ) {
400                 strcat( buf, "auth" );
401         } else if ( ACL_IS_COMPARE(access) ) {
402                 strcat( buf, "compare" );
403         } else if ( ACL_IS_SEARCH(access) ) {
404                 strcat( buf, "search" );
405         } else if ( ACL_IS_READ(access) ) {
406                 strcat( buf, "read" );
407         } else if ( ACL_IS_WRITE(access) ) {
408                 strcat( buf, "write" );
409
410         } else {
411                 strcat( buf, "unknown" );
412         }
413
414         return( buf );
415 }
416
417 int
418 str2access( char *str )
419 {
420         int     access;
421
422         ACL_CLR(access);
423
424         if ( strncasecmp( str, "self", 4 ) == 0 ) {
425                 ACL_SET_SELF(access);
426                 str += 4;
427         }
428
429         if ( strcasecmp( str, "none" ) == 0 ) {
430                 ACL_SET_NONE(access);
431         } else if ( strcasecmp( str, "auth" ) == 0 ) {
432                 ACL_SET_AUTH(access);
433         } else if ( strcasecmp( str, "compare" ) == 0 ) {
434                 ACL_SET_COMPARE(access);
435         } else if ( strcasecmp( str, "search" ) == 0 ) {
436                 ACL_SET_SEARCH(access);
437         } else if ( strcasecmp( str, "read" ) == 0 ) {
438                 ACL_SET_READ(access);
439         } else if ( strcasecmp( str, "write" ) == 0 ) {
440                 ACL_SET_WRITE(access);
441         } else {
442                 ACL_SET_INVALID(access);
443         }
444
445         return( access );
446 }
447
448 static void
449 acl_usage( void )
450 {
451         fprintf( stderr, "\n"
452                 "<access clause> ::= access to <what> [ by <who> <access> ]+ \n"
453                 "<what> ::= * | [dn=<regex>] [filter=<ldapfilter>] [attrs=<attrlist>]\n"
454                 "<attrlist> ::= <attr> | <attr> , <attrlist>\n"
455                 "<attr> ::= <attrname> | entry | children\n"
456                 "<who> ::= [ * | anonymous | self | dn=<regex> ]\n"
457                         "\t[dnattr=<attrname>]\n"
458                         "\t[group[/<objectclass>[/<attrname>]]=<regex>]\n"
459                         "\t[peername=<regex>] [sockname=<regex>]\n"
460                         "\t[domain=<regex>] [sockurl=<regex>]\n"
461 #ifdef SLAPD_ACI_ENABLED
462                         "\t[aci=<attrname>]\n"
463 #endif
464                 "<access> ::= [self]{none|auth|compare|search|read|write}\n"
465                 );
466         exit( EXIT_FAILURE );
467 }
468
469 static void
470 split(
471     char        *line,
472     int         splitchar,
473     char        **left,
474     char        **right
475 )
476 {
477         *left = line;
478         if ( (*right = strchr( line, splitchar )) != NULL ) {
479                 *((*right)++) = '\0';
480         }
481 }
482
483 static void
484 access_append( Access **l, Access *a )
485 {
486         for ( ; *l != NULL; l = &(*l)->a_next )
487                 ;       /* NULL */
488
489         *l = a;
490 }
491
492 static void
493 acl_append( AccessControl **l, AccessControl *a )
494 {
495         for ( ; *l != NULL; l = &(*l)->acl_next )
496                 ;       /* NULL */
497
498         *l = a;
499 }
500
501 #ifdef LDAP_DEBUG
502
503 static void
504 print_access( Access *b )
505 {
506         fprintf( stderr, "\tby" );
507
508         if ( b->a_dn_pat != NULL ) {
509                 if( strcmp(b->a_dn_pat, "anonymous") == 0 ) {
510                         fprintf( stderr, " anonymous" );
511
512                 } else if( strcmp(b->a_dn_pat, "self") == 0 ) {
513                         fprintf( stderr, " self" );
514
515                 } else {
516                         fprintf( stderr, " dn=%s", b->a_dn_pat );
517                 }
518         }
519
520         if ( b->a_dn_at != NULL ) {
521                 fprintf( stderr, " dnattr=%s", b->a_dn_at );
522         }
523
524         if ( b->a_group_pat != NULL ) {
525                 fprintf( stderr, " group: %s", b->a_group_pat );
526
527                 if ( b->a_group_oc ) {
528                         fprintf( stderr, " objectClass: %s", b->a_group_oc );
529
530                         if ( b->a_group_at ) {
531                                 fprintf( stderr, " attributeType: %s", b->a_group_at );
532                         }
533                 }
534     }
535
536         if ( b->a_peername_pat != NULL ) {
537                 fprintf( stderr, " peername=%s", b->a_peername_pat );
538         }
539         if ( b->a_sockname_pat != NULL ) {
540                 fprintf( stderr, " sockname=%s", b->a_sockname_pat );
541         }
542
543         if ( b->a_domain_pat != NULL ) {
544                 fprintf( stderr, " domain=%s", b->a_domain_pat );
545         }
546
547         if ( b->a_sockurl_pat != NULL ) {
548                 fprintf( stderr, " sockurl=%s", b->a_sockurl_pat );
549         }
550
551 #ifdef SLAPD_ACI_ENABLED
552         if ( b->a_aci_at != NULL ) {
553                 fprintf( stderr, " aci=%s", b->a_aci_at );
554         }
555 #endif
556
557         fprintf( stderr, "\n" );
558 }
559
560 static void
561 print_acl( AccessControl *a )
562 {
563         int             i;
564         Access  *b;
565
566         if ( a == NULL ) {
567                 fprintf( stderr, "NULL\n" );
568         }
569         fprintf( stderr, "ACL: access to" );
570         if ( a->acl_filter != NULL ) {
571                 fprintf(  stderr," filter=" );
572                 filter_print( a->acl_filter );
573         }
574         if ( a->acl_dn_pat != NULL ) {
575                 fprintf( stderr, " dn=" );
576                 fprintf( stderr, a->acl_dn_pat );
577         }
578         if ( a->acl_attrs != NULL ) {
579                 int     first = 1;
580
581                 fprintf( stderr, "\n attrs=" );
582                 for ( i = 0; a->acl_attrs[i] != NULL; i++ ) {
583                         if ( ! first ) {
584                                 fprintf( stderr, "," );
585                         }
586                         fprintf( stderr, a->acl_attrs[i] );
587                         first = 0;
588                 }
589         }
590         fprintf( stderr, "\n" );
591         for ( b = a->acl_access; b != NULL; b = b->a_next ) {
592                 print_access( b );
593         }
594         fprintf( stderr, "\n" );
595 }
596
597 #endif /* LDAP_DEBUG */