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