1/*	$NetBSD: aclparse.c,v 1.1.1.3 2010/12/12 15:22:15 adam Exp $	*/
2
3/* aclparse.c - routines to parse and check acl's */
4/* OpenLDAP: pkg/ldap/servers/slapd/aclparse.c,v 1.198.2.13 2010/04/15 22:26:06 quanah Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1998-2010 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18/* Portions Copyright (c) 1995 Regents of the University of Michigan.
19 * All rights reserved.
20 *
21 * Redistribution and use in source and binary forms are permitted
22 * provided that this notice is preserved and that due credit is given
23 * to the University of Michigan at Ann Arbor. The name of the University
24 * may not be used to endorse or promote products derived from this
25 * software without specific prior written permission. This software
26 * is provided ``as is'' without express or implied warranty.
27 */
28
29#include "portable.h"
30
31#include <stdio.h>
32
33#include <ac/ctype.h>
34#include <ac/regex.h>
35#include <ac/socket.h>
36#include <ac/string.h>
37#include <ac/unistd.h>
38
39#include "slap.h"
40#include "lber_pvt.h"
41#include "lutil.h"
42
43static const char style_base[] = "base";
44const char *style_strings[] = {
45	"regex",
46	"expand",
47	"exact",
48	"one",
49	"subtree",
50	"children",
51	"level",
52	"attrof",
53	"anonymous",
54	"users",
55	"self",
56	"ip",
57	"ipv6",
58	"path",
59	NULL
60};
61
62static void		split(char *line, int splitchar, char **left, char **right);
63static void		access_append(Access **l, Access *a);
64static void		access_free( Access *a );
65static int		acl_usage(void);
66
67static void		acl_regex_normalized_dn(const char *src, struct berval *pat);
68
69#ifdef LDAP_DEBUG
70static void		print_acl(Backend *be, AccessControl *a);
71#endif
72
73static int		check_scope( BackendDB *be, AccessControl *a );
74
75#ifdef SLAP_DYNACL
76static int
77slap_dynacl_config(
78	const char *fname,
79	int lineno,
80	Access *b,
81	const char *name,
82	const char *opts,
83	slap_style_t sty,
84	const char *right )
85{
86	slap_dynacl_t	*da, *tmp;
87	int		rc = 0;
88
89	for ( da = b->a_dynacl; da; da = da->da_next ) {
90		if ( strcasecmp( da->da_name, name ) == 0 ) {
91			Debug( LDAP_DEBUG_ANY,
92				"%s: line %d: dynacl \"%s\" already specified.\n",
93				fname, lineno, name );
94			return acl_usage();
95		}
96	}
97
98	da = slap_dynacl_get( name );
99	if ( da == NULL ) {
100		return -1;
101	}
102
103	tmp = ch_malloc( sizeof( slap_dynacl_t ) );
104	*tmp = *da;
105
106	if ( tmp->da_parse ) {
107		rc = ( *tmp->da_parse )( fname, lineno, opts, sty, right, &tmp->da_private );
108		if ( rc ) {
109			ch_free( tmp );
110			return rc;
111		}
112	}
113
114	tmp->da_next = b->a_dynacl;
115	b->a_dynacl = tmp;
116
117	return 0;
118}
119#endif /* SLAP_DYNACL */
120
121static void
122regtest(const char *fname, int lineno, char *pat) {
123	int e;
124	regex_t re;
125
126	char		buf[ SLAP_TEXT_BUFLEN ];
127	unsigned	size;
128
129	char *sp;
130	char *dp;
131	int  flag;
132
133	sp = pat;
134	dp = buf;
135	size = 0;
136	buf[0] = '\0';
137
138	for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
139		if (flag) {
140			if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
141				*dp++ = *sp;
142				size++;
143			}
144			flag = 0;
145
146		} else {
147			if (*sp == '$') {
148				flag = 1;
149			} else {
150				*dp++ = *sp;
151				size++;
152			}
153		}
154	}
155
156	*dp = '\0';
157	if ( size >= (sizeof(buf) - 1) ) {
158		Debug( LDAP_DEBUG_ANY,
159			"%s: line %d: regular expression \"%s\" too large\n",
160			fname, lineno, pat );
161		(void)acl_usage();
162		exit( EXIT_FAILURE );
163	}
164
165	if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
166		char error[ SLAP_TEXT_BUFLEN ];
167
168		regerror(e, &re, error, sizeof(error));
169
170		snprintf( buf, sizeof( buf ),
171			"regular expression \"%s\" bad because of %s",
172			pat, error );
173		Debug( LDAP_DEBUG_ANY,
174			"%s: line %d: %s\n",
175			fname, lineno, buf );
176		acl_usage();
177		exit( EXIT_FAILURE );
178	}
179	regfree(&re);
180}
181
182/*
183 * Experimental
184 *
185 * Check if the pattern of an ACL, if any, matches the scope
186 * of the backend it is defined within.
187 */
188#define	ACL_SCOPE_UNKNOWN	(-2)
189#define	ACL_SCOPE_ERR		(-1)
190#define	ACL_SCOPE_OK		(0)
191#define	ACL_SCOPE_PARTIAL	(1)
192#define	ACL_SCOPE_WARN		(2)
193
194static int
195check_scope( BackendDB *be, AccessControl *a )
196{
197	ber_len_t	patlen;
198	struct berval	dn;
199
200	dn = be->be_nsuffix[0];
201
202	if ( BER_BVISEMPTY( &dn ) ) {
203		return ACL_SCOPE_OK;
204	}
205
206	if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
207			a->acl_dn_style != ACL_STYLE_REGEX )
208	{
209		slap_style_t	style = a->acl_dn_style;
210
211		if ( style == ACL_STYLE_REGEX ) {
212			char		dnbuf[SLAP_LDAPDN_MAXLEN + 2];
213			char		rebuf[SLAP_LDAPDN_MAXLEN + 1];
214			ber_len_t	rebuflen;
215			regex_t		re;
216			int		rc;
217
218			/* add trailing '$' to database suffix to form
219			 * a simple trial regex pattern "<suffix>$" */
220			AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
221				be->be_nsuffix[0].bv_len );
222			dnbuf[be->be_nsuffix[0].bv_len] = '$';
223			dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';
224
225			if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
226				return ACL_SCOPE_WARN;
227			}
228
229			/* remove trailing ')$', if any, from original
230			 * regex pattern */
231			rebuflen = a->acl_dn_pat.bv_len;
232			AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
233			if ( rebuf[rebuflen - 1] == '$' ) {
234				rebuf[--rebuflen] = '\0';
235			}
236			while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
237				rebuf[--rebuflen] = '\0';
238			}
239			if ( rebuflen == be->be_nsuffix[0].bv_len ) {
240				rc = ACL_SCOPE_WARN;
241				goto regex_done;
242			}
243
244			/* not a clear indication of scoping error, though */
245			rc = regexec( &re, rebuf, 0, NULL, 0 )
246				? ACL_SCOPE_WARN : ACL_SCOPE_OK;
247
248regex_done:;
249			regfree( &re );
250			return rc;
251		}
252
253		patlen = a->acl_dn_pat.bv_len;
254		/* If backend suffix is longer than pattern,
255		 * it is a potential mismatch (in the sense
256		 * that a superior naming context could
257		 * match */
258		if ( dn.bv_len > patlen ) {
259			/* base is blatantly wrong */
260			if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;
261
262			/* a style of one can be wrong if there is
263			 * more than one level between the suffix
264			 * and the pattern */
265			if ( style == ACL_STYLE_ONE ) {
266				ber_len_t	rdnlen = 0;
267				int		sep = 0;
268
269				if ( patlen > 0 ) {
270					if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
271						return ACL_SCOPE_ERR;
272					}
273					sep = 1;
274				}
275
276				rdnlen = dn_rdnlen( NULL, &dn );
277				if ( rdnlen != dn.bv_len - patlen - sep )
278					return ACL_SCOPE_ERR;
279			}
280
281			/* if the trailing part doesn't match,
282			 * then it's an error */
283			if ( strcmp( a->acl_dn_pat.bv_val,
284				&dn.bv_val[dn.bv_len - patlen] ) != 0 )
285			{
286				return ACL_SCOPE_ERR;
287			}
288
289			return ACL_SCOPE_PARTIAL;
290		}
291
292		switch ( style ) {
293		case ACL_STYLE_BASE:
294		case ACL_STYLE_ONE:
295		case ACL_STYLE_CHILDREN:
296		case ACL_STYLE_SUBTREE:
297			break;
298
299		default:
300			assert( 0 );
301			break;
302		}
303
304		if ( dn.bv_len < patlen &&
305			!DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
306		{
307			return ACL_SCOPE_ERR;
308		}
309
310		if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
311			!= 0 )
312		{
313			return ACL_SCOPE_ERR;
314		}
315
316		return ACL_SCOPE_OK;
317	}
318
319	return ACL_SCOPE_UNKNOWN;
320}
321
322int
323parse_acl(
324	Backend	*be,
325	const char	*fname,
326	int		lineno,
327	int		argc,
328	char		**argv,
329	int		pos )
330{
331	int		i;
332	char		*left, *right, *style;
333	struct berval	bv;
334	AccessControl	*a = NULL;
335	Access	*b = NULL;
336	int rc;
337	const char *text;
338
339	for ( i = 1; i < argc; i++ ) {
340		/* to clause - select which entries are protected */
341		if ( strcasecmp( argv[i], "to" ) == 0 ) {
342			if ( a != NULL ) {
343				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
344					"only one to clause allowed in access line\n",
345				    fname, lineno, 0 );
346				goto fail;
347			}
348			a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
349			a->acl_attrval_style = ACL_STYLE_NONE;
350			for ( ++i; i < argc; i++ ) {
351				if ( strcasecmp( argv[i], "by" ) == 0 ) {
352					i--;
353					break;
354				}
355
356				if ( strcasecmp( argv[i], "*" ) == 0 ) {
357					if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
358						a->acl_dn_style != ACL_STYLE_REGEX )
359					{
360						Debug( LDAP_DEBUG_ANY,
361							"%s: line %d: dn pattern"
362							" already specified in to clause.\n",
363							fname, lineno, 0 );
364						goto fail;
365					}
366
367					ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
368					continue;
369				}
370
371				split( argv[i], '=', &left, &right );
372				split( left, '.', &left, &style );
373
374				if ( right == NULL ) {
375					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
376						"missing \"=\" in \"%s\" in to clause\n",
377					    fname, lineno, left );
378					goto fail;
379				}
380
381				if ( strcasecmp( left, "dn" ) == 0 ) {
382					if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
383						a->acl_dn_style != ACL_STYLE_REGEX )
384					{
385						Debug( LDAP_DEBUG_ANY,
386							"%s: line %d: dn pattern"
387							" already specified in to clause.\n",
388							fname, lineno, 0 );
389						goto fail;
390					}
391
392					if ( style == NULL || *style == '\0' ||
393						strcasecmp( style, "baseObject" ) == 0 ||
394						strcasecmp( style, "base" ) == 0 ||
395						strcasecmp( style, "exact" ) == 0 )
396					{
397						a->acl_dn_style = ACL_STYLE_BASE;
398						ber_str2bv( right, 0, 1, &a->acl_dn_pat );
399
400					} else if ( strcasecmp( style, "oneLevel" ) == 0 ||
401						strcasecmp( style, "one" ) == 0 )
402					{
403						a->acl_dn_style = ACL_STYLE_ONE;
404						ber_str2bv( right, 0, 1, &a->acl_dn_pat );
405
406					} else if ( strcasecmp( style, "subtree" ) == 0 ||
407						strcasecmp( style, "sub" ) == 0 )
408					{
409						if( *right == '\0' ) {
410							ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
411
412						} else {
413							a->acl_dn_style = ACL_STYLE_SUBTREE;
414							ber_str2bv( right, 0, 1, &a->acl_dn_pat );
415						}
416
417					} else if ( strcasecmp( style, "children" ) == 0 ) {
418						a->acl_dn_style = ACL_STYLE_CHILDREN;
419						ber_str2bv( right, 0, 1, &a->acl_dn_pat );
420
421					} else if ( strcasecmp( style, "regex" ) == 0 ) {
422						a->acl_dn_style = ACL_STYLE_REGEX;
423
424						if ( *right == '\0' ) {
425							/* empty regex should match empty DN */
426							a->acl_dn_style = ACL_STYLE_BASE;
427							ber_str2bv( right, 0, 1, &a->acl_dn_pat );
428
429						} else if ( strcmp(right, "*") == 0
430							|| strcmp(right, ".*") == 0
431							|| strcmp(right, ".*$") == 0
432							|| strcmp(right, "^.*") == 0
433							|| strcmp(right, "^.*$") == 0
434							|| strcmp(right, ".*$$") == 0
435							|| strcmp(right, "^.*$$") == 0 )
436						{
437							ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
438
439						} else {
440							acl_regex_normalized_dn( right, &a->acl_dn_pat );
441						}
442
443					} else {
444						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
445							"unknown dn style \"%s\" in to clause\n",
446						    fname, lineno, style );
447						goto fail;
448					}
449
450					continue;
451				}
452
453				if ( strcasecmp( left, "filter" ) == 0 ) {
454					if ( (a->acl_filter = str2filter( right )) == NULL ) {
455						Debug( LDAP_DEBUG_ANY,
456				"%s: line %d: bad filter \"%s\" in to clause\n",
457						    fname, lineno, right );
458						goto fail;
459					}
460
461				} else if ( strcasecmp( left, "attr" ) == 0		/* TOLERATED */
462						|| strcasecmp( left, "attrs" ) == 0 )	/* DOCUMENTED */
463				{
464					if ( strcasecmp( left, "attr" ) == 0 ) {
465						Debug( LDAP_DEBUG_ANY,
466							"%s: line %d: \"attr\" "
467							"is deprecated (and undocumented); "
468							"use \"attrs\" instead.\n",
469							fname, lineno, 0 );
470					}
471
472					a->acl_attrs = str2anlist( a->acl_attrs,
473						right, "," );
474					if ( a->acl_attrs == NULL ) {
475						Debug( LDAP_DEBUG_ANY,
476				"%s: line %d: unknown attr \"%s\" in to clause\n",
477						    fname, lineno, right );
478						goto fail;
479					}
480
481				} else if ( strncasecmp( left, "val", 3 ) == 0 ) {
482					struct berval	bv;
483					char		*mr;
484
485					if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
486						Debug( LDAP_DEBUG_ANY,
487				"%s: line %d: attr val already specified in to clause.\n",
488							fname, lineno, 0 );
489						goto fail;
490					}
491					if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
492					{
493						Debug( LDAP_DEBUG_ANY,
494				"%s: line %d: attr val requires a single attribute.\n",
495							fname, lineno, 0 );
496						goto fail;
497					}
498
499					ber_str2bv( right, 0, 0, &bv );
500					a->acl_attrval_style = ACL_STYLE_BASE;
501
502					mr = strchr( left, '/' );
503					if ( mr != NULL ) {
504						mr[ 0 ] = '\0';
505						mr++;
506
507						a->acl_attrval_mr = mr_find( mr );
508						if ( a->acl_attrval_mr == NULL ) {
509							Debug( LDAP_DEBUG_ANY, "%s: line %d: "
510								"invalid matching rule \"%s\".\n",
511								fname, lineno, mr );
512							goto fail;
513						}
514
515						if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) )
516						{
517							char	buf[ SLAP_TEXT_BUFLEN ];
518
519							snprintf( buf, sizeof( buf ),
520								"matching rule \"%s\" use "
521								"with attr \"%s\" not appropriate.",
522								mr, a->acl_attrs[ 0 ].an_name.bv_val );
523
524
525							Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
526								fname, lineno, buf );
527							goto fail;
528						}
529					}
530
531					if ( style != NULL ) {
532						if ( strcasecmp( style, "regex" ) == 0 ) {
533							int e = regcomp( &a->acl_attrval_re, bv.bv_val,
534								REG_EXTENDED | REG_ICASE );
535							if ( e ) {
536								char	err[SLAP_TEXT_BUFLEN],
537									buf[ SLAP_TEXT_BUFLEN ];
538
539								regerror( e, &a->acl_attrval_re, err, sizeof( err ) );
540
541								snprintf( buf, sizeof( buf ),
542									"regular expression \"%s\" bad because of %s",
543									right, err );
544
545								Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
546									fname, lineno, buf );
547								goto fail;
548							}
549							a->acl_attrval_style = ACL_STYLE_REGEX;
550
551						} else {
552							/* FIXME: if the attribute has DN syntax, we might
553							 * allow one, subtree and children styles as well */
554							if ( !strcasecmp( style, "base" ) ||
555								!strcasecmp( style, "exact" ) ) {
556								a->acl_attrval_style = ACL_STYLE_BASE;
557
558							} else if ( a->acl_attrs[0].an_desc->ad_type->
559								sat_syntax == slap_schema.si_syn_distinguishedName )
560							{
561								if ( !strcasecmp( style, "baseObject" ) ||
562									!strcasecmp( style, "base" ) )
563								{
564									a->acl_attrval_style = ACL_STYLE_BASE;
565								} else if ( !strcasecmp( style, "onelevel" ) ||
566									!strcasecmp( style, "one" ) )
567								{
568									a->acl_attrval_style = ACL_STYLE_ONE;
569								} else if ( !strcasecmp( style, "subtree" ) ||
570									!strcasecmp( style, "sub" ) )
571								{
572									a->acl_attrval_style = ACL_STYLE_SUBTREE;
573								} else if ( !strcasecmp( style, "children" ) ) {
574									a->acl_attrval_style = ACL_STYLE_CHILDREN;
575								} else {
576									char	buf[ SLAP_TEXT_BUFLEN ];
577
578									snprintf( buf, sizeof( buf ),
579										"unknown val.<style> \"%s\" for attributeType \"%s\" "
580											"with DN syntax.",
581										style,
582										a->acl_attrs[0].an_desc->ad_cname.bv_val );
583
584									Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
585										"%s: line %d: %s\n",
586										fname, lineno, buf );
587									goto fail;
588								}
589
590								rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL );
591								if ( rc != LDAP_SUCCESS ) {
592									char	buf[ SLAP_TEXT_BUFLEN ];
593
594									snprintf( buf, sizeof( buf ),
595										"unable to normalize DN \"%s\" "
596										"for attributeType \"%s\" (%d).",
597										bv.bv_val,
598										a->acl_attrs[0].an_desc->ad_cname.bv_val,
599										rc );
600									Debug( LDAP_DEBUG_ANY,
601										"%s: line %d: %s\n",
602										fname, lineno, buf );
603									goto fail;
604								}
605
606							} else {
607								char	buf[ SLAP_TEXT_BUFLEN ];
608
609								snprintf( buf, sizeof( buf ),
610									"unknown val.<style> \"%s\" for attributeType \"%s\".",
611									style, a->acl_attrs[0].an_desc->ad_cname.bv_val );
612								Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
613									"%s: line %d: %s\n",
614									fname, lineno, buf );
615								goto fail;
616							}
617						}
618					}
619
620					/* Check for appropriate matching rule */
621					if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
622						ber_dupbv( &a->acl_attrval, &bv );
623
624					} else if ( BER_BVISNULL( &a->acl_attrval ) ) {
625						int		rc;
626						const char	*text;
627
628						if ( a->acl_attrval_mr == NULL ) {
629							a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality;
630						}
631
632						if ( a->acl_attrval_mr == NULL ) {
633							Debug( LDAP_DEBUG_ANY, "%s: line %d: "
634								"attr \"%s\" does not have an EQUALITY matching rule.\n",
635								fname, lineno, a->acl_attrs[ 0 ].an_name.bv_val );
636							goto fail;
637						}
638
639						rc = asserted_value_validate_normalize(
640							a->acl_attrs[ 0 ].an_desc,
641							a->acl_attrval_mr,
642							SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
643							&bv,
644							&a->acl_attrval,
645							&text,
646							NULL );
647						if ( rc != LDAP_SUCCESS ) {
648							char	buf[ SLAP_TEXT_BUFLEN ];
649
650							snprintf( buf, sizeof( buf ), "%s: line %d: "
651								" attr \"%s\" normalization failed (%d: %s)",
652								fname, lineno,
653								a->acl_attrs[ 0 ].an_name.bv_val, rc, text );
654							Debug( LDAP_DEBUG_ANY, "%s: line %d: %s.\n",
655								fname, lineno, buf );
656							goto fail;
657						}
658					}
659
660				} else {
661					Debug( LDAP_DEBUG_ANY,
662						"%s: line %d: expecting <what> got \"%s\"\n",
663					    fname, lineno, left );
664					goto fail;
665				}
666			}
667
668			if ( !BER_BVISNULL( &a->acl_dn_pat ) &&
669					ber_bvccmp( &a->acl_dn_pat, '*' ) )
670			{
671				free( a->acl_dn_pat.bv_val );
672				BER_BVZERO( &a->acl_dn_pat );
673				a->acl_dn_style = ACL_STYLE_REGEX;
674			}
675
676			if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
677					a->acl_dn_style != ACL_STYLE_REGEX )
678			{
679				if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
680					struct berval bv;
681					rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
682					if ( rc != LDAP_SUCCESS ) {
683						Debug( LDAP_DEBUG_ANY,
684							"%s: line %d: bad DN \"%s\" in to DN clause\n",
685							fname, lineno, a->acl_dn_pat.bv_val );
686						goto fail;
687					}
688					free( a->acl_dn_pat.bv_val );
689					a->acl_dn_pat = bv;
690
691				} else {
692					int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
693						REG_EXTENDED | REG_ICASE );
694					if ( e ) {
695						char	err[ SLAP_TEXT_BUFLEN ],
696							buf[ SLAP_TEXT_BUFLEN ];
697
698						regerror( e, &a->acl_dn_re, err, sizeof( err ) );
699						snprintf( buf, sizeof( buf ),
700							"regular expression \"%s\" bad because of %s",
701							right, err );
702						Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
703							fname, lineno, buf );
704						goto fail;
705					}
706				}
707			}
708
709		/* by clause - select who has what access to entries */
710		} else if ( strcasecmp( argv[i], "by" ) == 0 ) {
711			if ( a == NULL ) {
712				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
713					"to clause required before by clause in access line\n",
714					fname, lineno, 0 );
715				goto fail;
716			}
717
718			/*
719			 * by clause consists of <who> and <access>
720			 */
721
722			if ( ++i == argc ) {
723				Debug( LDAP_DEBUG_ANY,
724					"%s: line %d: premature EOL: expecting <who>\n",
725					fname, lineno, 0 );
726				goto fail;
727			}
728
729			b = (Access *) ch_calloc( 1, sizeof(Access) );
730
731			ACL_INVALIDATE( b->a_access_mask );
732
733			/* get <who> */
734			for ( ; i < argc; i++ ) {
735				slap_style_t	sty = ACL_STYLE_REGEX;
736				char		*style_modifier = NULL;
737				char		*style_level = NULL;
738				int		level = 0;
739				int		expand = 0;
740				slap_dn_access	*bdn = &b->a_dn;
741				int		is_realdn = 0;
742
743				split( argv[i], '=', &left, &right );
744				split( left, '.', &left, &style );
745				if ( style ) {
746					split( style, ',', &style, &style_modifier );
747
748					if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
749						split( style, '{', &style, &style_level );
750						if ( style_level != NULL ) {
751							char *p = strchr( style_level, '}' );
752							if ( p == NULL ) {
753								Debug( LDAP_DEBUG_ANY,
754									"%s: line %d: premature eol: "
755									"expecting closing '}' in \"level{n}\"\n",
756									fname, lineno, 0 );
757								goto fail;
758							} else if ( p == style_level ) {
759								Debug( LDAP_DEBUG_ANY,
760									"%s: line %d: empty level "
761									"in \"level{n}\"\n",
762									fname, lineno, 0 );
763								goto fail;
764							}
765							p[0] = '\0';
766						}
767					}
768				}
769
770				if ( style == NULL || *style == '\0' ||
771					strcasecmp( style, "exact" ) == 0 ||
772					strcasecmp( style, "baseObject" ) == 0 ||
773					strcasecmp( style, "base" ) == 0 )
774				{
775					sty = ACL_STYLE_BASE;
776
777				} else if ( strcasecmp( style, "onelevel" ) == 0 ||
778					strcasecmp( style, "one" ) == 0 )
779				{
780					sty = ACL_STYLE_ONE;
781
782				} else if ( strcasecmp( style, "subtree" ) == 0 ||
783					strcasecmp( style, "sub" ) == 0 )
784				{
785					sty = ACL_STYLE_SUBTREE;
786
787				} else if ( strcasecmp( style, "children" ) == 0 ) {
788					sty = ACL_STYLE_CHILDREN;
789
790				} else if ( strcasecmp( style, "level" ) == 0 )
791				{
792					if ( lutil_atoi( &level, style_level ) != 0 ) {
793						Debug( LDAP_DEBUG_ANY,
794							"%s: line %d: unable to parse level "
795							"in \"level{n}\"\n",
796							fname, lineno, 0 );
797						goto fail;
798					}
799
800					sty = ACL_STYLE_LEVEL;
801
802				} else if ( strcasecmp( style, "regex" ) == 0 ) {
803					sty = ACL_STYLE_REGEX;
804
805				} else if ( strcasecmp( style, "expand" ) == 0 ) {
806					sty = ACL_STYLE_EXPAND;
807
808				} else if ( strcasecmp( style, "ip" ) == 0 ) {
809					sty = ACL_STYLE_IP;
810
811				} else if ( strcasecmp( style, "ipv6" ) == 0 ) {
812#ifndef LDAP_PF_INET6
813					Debug( LDAP_DEBUG_ANY,
814						"%s: line %d: IPv6 not supported\n",
815						fname, lineno, 0 );
816#endif /* ! LDAP_PF_INET6 */
817					sty = ACL_STYLE_IPV6;
818
819				} else if ( strcasecmp( style, "path" ) == 0 ) {
820					sty = ACL_STYLE_PATH;
821#ifndef LDAP_PF_LOCAL
822					Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
823						"%s: line %d: "
824						"\"path\" style modifier is useless without local.\n",
825						fname, lineno, 0 );
826					goto fail;
827#endif /* LDAP_PF_LOCAL */
828
829				} else {
830					Debug( LDAP_DEBUG_ANY,
831						"%s: line %d: unknown style \"%s\" in by clause\n",
832						fname, lineno, style );
833					goto fail;
834				}
835
836				if ( style_modifier &&
837					strcasecmp( style_modifier, "expand" ) == 0 )
838				{
839					switch ( sty ) {
840					case ACL_STYLE_REGEX:
841						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
842							"\"regex\" style implies \"expand\" modifier.\n",
843							fname, lineno, 0 );
844						goto fail;
845						break;
846
847					case ACL_STYLE_EXPAND:
848						break;
849
850					default:
851						/* we'll see later if it's pertinent */
852						expand = 1;
853						break;
854					}
855				}
856
857				if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
858					is_realdn = 1;
859					bdn = &b->a_realdn;
860					left += STRLENOF( "real" );
861				}
862
863				if ( strcasecmp( left, "*" ) == 0 ) {
864					if ( is_realdn ) {
865						goto fail;
866					}
867
868					ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
869					sty = ACL_STYLE_REGEX;
870
871				} else if ( strcasecmp( left, "anonymous" ) == 0 ) {
872					ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
873					sty = ACL_STYLE_ANONYMOUS;
874
875				} else if ( strcasecmp( left, "users" ) == 0 ) {
876					ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
877					sty = ACL_STYLE_USERS;
878
879				} else if ( strcasecmp( left, "self" ) == 0 ) {
880					ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
881					sty = ACL_STYLE_SELF;
882
883				} else if ( strcasecmp( left, "dn" ) == 0 ) {
884					if ( sty == ACL_STYLE_REGEX ) {
885						bdn->a_style = ACL_STYLE_REGEX;
886						if ( right == NULL ) {
887							/* no '=' */
888							ber_str2bv("users",
889								STRLENOF( "users" ),
890								1, &bv);
891							bdn->a_style = ACL_STYLE_USERS;
892
893						} else if (*right == '\0' ) {
894							/* dn="" */
895							ber_str2bv("anonymous",
896								STRLENOF( "anonymous" ),
897								1, &bv);
898							bdn->a_style = ACL_STYLE_ANONYMOUS;
899
900						} else if ( strcmp( right, "*" ) == 0 ) {
901							/* dn=* */
902							/* any or users?  users for now */
903							ber_str2bv("users",
904								STRLENOF( "users" ),
905								1, &bv);
906							bdn->a_style = ACL_STYLE_USERS;
907
908						} else if ( strcmp( right, ".+" ) == 0
909							|| strcmp( right, "^.+" ) == 0
910							|| strcmp( right, ".+$" ) == 0
911							|| strcmp( right, "^.+$" ) == 0
912							|| strcmp( right, ".+$$" ) == 0
913							|| strcmp( right, "^.+$$" ) == 0 )
914						{
915							ber_str2bv("users",
916								STRLENOF( "users" ),
917								1, &bv);
918							bdn->a_style = ACL_STYLE_USERS;
919
920						} else if ( strcmp( right, ".*" ) == 0
921							|| strcmp( right, "^.*" ) == 0
922							|| strcmp( right, ".*$" ) == 0
923							|| strcmp( right, "^.*$" ) == 0
924							|| strcmp( right, ".*$$" ) == 0
925							|| strcmp( right, "^.*$$" ) == 0 )
926						{
927							ber_str2bv("*",
928								STRLENOF( "*" ),
929								1, &bv);
930
931						} else {
932							acl_regex_normalized_dn( right, &bv );
933							if ( !ber_bvccmp( &bv, '*' ) ) {
934								regtest( fname, lineno, bv.bv_val );
935							}
936						}
937
938					} else if ( right == NULL || *right == '\0' ) {
939						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
940							"missing \"=\" in (or value after) \"%s\" "
941							"in by clause\n",
942							fname, lineno, left );
943						goto fail;
944
945					} else {
946						ber_str2bv( right, 0, 1, &bv );
947					}
948
949				} else {
950					BER_BVZERO( &bv );
951				}
952
953				if ( !BER_BVISNULL( &bv ) ) {
954					if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
955						Debug( LDAP_DEBUG_ANY,
956							"%s: line %d: dn pattern already specified.\n",
957							fname, lineno, 0 );
958						goto fail;
959					}
960
961					if ( sty != ACL_STYLE_REGEX &&
962							sty != ACL_STYLE_ANONYMOUS &&
963							sty != ACL_STYLE_USERS &&
964							sty != ACL_STYLE_SELF &&
965							expand == 0 )
966					{
967						rc = dnNormalize(0, NULL, NULL,
968							&bv, &bdn->a_pat, NULL);
969						if ( rc != LDAP_SUCCESS ) {
970							Debug( LDAP_DEBUG_ANY,
971								"%s: line %d: bad DN \"%s\" in by DN clause\n",
972								fname, lineno, bv.bv_val );
973							goto fail;
974						}
975						free( bv.bv_val );
976						if ( sty == ACL_STYLE_BASE
977							&& be != NULL
978							&& !BER_BVISNULL( &be->be_rootndn )
979							&& dn_match( &bdn->a_pat, &be->be_rootndn ) )
980						{
981							Debug( LDAP_DEBUG_ANY,
982								"%s: line %d: rootdn is always granted "
983								"unlimited privileges.\n",
984								fname, lineno, 0 );
985						}
986
987					} else {
988						bdn->a_pat = bv;
989					}
990					bdn->a_style = sty;
991					if ( expand ) {
992						char	*exp;
993						int	gotit = 0;
994
995						for ( exp = strchr( bdn->a_pat.bv_val, '$' );
996							exp && (ber_len_t)(exp - bdn->a_pat.bv_val)
997								< bdn->a_pat.bv_len;
998							exp = strchr( exp, '$' ) )
999						{
1000							if ( ( isdigit( (unsigned char) exp[ 1 ] ) ||
1001								    exp[ 1 ] == '{' ) ) {
1002								gotit = 1;
1003								break;
1004							}
1005						}
1006
1007						if ( gotit == 1 ) {
1008							bdn->a_expand = expand;
1009
1010						} else {
1011							Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1012								"\"expand\" used with no expansions in \"pattern\".\n",
1013								fname, lineno, 0 );
1014							goto fail;
1015						}
1016					}
1017					if ( sty == ACL_STYLE_SELF ) {
1018						bdn->a_self_level = level;
1019
1020					} else {
1021						if ( level < 0 ) {
1022							Debug( LDAP_DEBUG_ANY,
1023								"%s: line %d: bad negative level \"%d\" "
1024								"in by DN clause\n",
1025								fname, lineno, level );
1026							goto fail;
1027						} else if ( level == 1 ) {
1028							Debug( LDAP_DEBUG_ANY,
1029								"%s: line %d: \"onelevel\" should be used "
1030								"instead of \"level{1}\" in by DN clause\n",
1031								fname, lineno, 0 );
1032						} else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
1033							Debug( LDAP_DEBUG_ANY,
1034								"%s: line %d: \"base\" should be used "
1035								"instead of \"level{0}\" in by DN clause\n",
1036								fname, lineno, 0 );
1037						}
1038
1039						bdn->a_level = level;
1040					}
1041					continue;
1042				}
1043
1044				if ( strcasecmp( left, "dnattr" ) == 0 ) {
1045					if ( right == NULL || right[0] == '\0' ) {
1046						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1047							"missing \"=\" in (or value after) \"%s\" "
1048							"in by clause\n",
1049							fname, lineno, left );
1050						goto fail;
1051					}
1052
1053					if( bdn->a_at != NULL ) {
1054						Debug( LDAP_DEBUG_ANY,
1055							"%s: line %d: dnattr already specified.\n",
1056							fname, lineno, 0 );
1057						goto fail;
1058					}
1059
1060					rc = slap_str2ad( right, &bdn->a_at, &text );
1061
1062					if( rc != LDAP_SUCCESS ) {
1063						char	buf[ SLAP_TEXT_BUFLEN ];
1064
1065						snprintf( buf, sizeof( buf ),
1066							"dnattr \"%s\": %s",
1067							right, text );
1068						Debug( LDAP_DEBUG_ANY,
1069							"%s: line %d: %s\n",
1070							fname, lineno, buf );
1071						goto fail;
1072					}
1073
1074
1075					if( !is_at_syntax( bdn->a_at->ad_type,
1076						SLAPD_DN_SYNTAX ) &&
1077						!is_at_syntax( bdn->a_at->ad_type,
1078						SLAPD_NAMEUID_SYNTAX ))
1079					{
1080						char	buf[ SLAP_TEXT_BUFLEN ];
1081
1082						snprintf( buf, sizeof( buf ),
1083							"dnattr \"%s\": "
1084							"inappropriate syntax: %s\n",
1085							right,
1086							bdn->a_at->ad_type->sat_syntax_oid );
1087						Debug( LDAP_DEBUG_ANY,
1088							"%s: line %d: %s\n",
1089							fname, lineno, buf );
1090						goto fail;
1091					}
1092
1093					if( bdn->a_at->ad_type->sat_equality == NULL ) {
1094						Debug( LDAP_DEBUG_ANY,
1095							"%s: line %d: dnattr \"%s\": "
1096							"inappropriate matching (no EQUALITY)\n",
1097							fname, lineno, right );
1098						goto fail;
1099					}
1100
1101					continue;
1102				}
1103
1104				if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
1105					char *name = NULL;
1106					char *value = NULL;
1107					char *attr_name = SLAPD_GROUP_ATTR;
1108
1109					switch ( sty ) {
1110					case ACL_STYLE_REGEX:
1111						/* legacy, tolerated */
1112						Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
1113							"%s: line %d: "
1114							"deprecated group style \"regex\"; "
1115							"use \"expand\" instead.\n",
1116							fname, lineno, 0 );
1117						sty = ACL_STYLE_EXPAND;
1118						break;
1119
1120					case ACL_STYLE_BASE:
1121						/* legal, traditional */
1122					case ACL_STYLE_EXPAND:
1123						/* legal, substring expansion; supersedes regex */
1124						break;
1125
1126					default:
1127						/* unknown */
1128						Debug( LDAP_DEBUG_ANY,
1129							"%s: line %d: "
1130							"inappropriate style \"%s\" in by clause.\n",
1131							fname, lineno, style );
1132						goto fail;
1133					}
1134
1135					if ( right == NULL || right[0] == '\0' ) {
1136						Debug( LDAP_DEBUG_ANY,
1137							"%s: line %d: "
1138							"missing \"=\" in (or value after) \"%s\" "
1139							"in by clause.\n",
1140							fname, lineno, left );
1141						goto fail;
1142					}
1143
1144					if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
1145						Debug( LDAP_DEBUG_ANY,
1146							"%s: line %d: group pattern already specified.\n",
1147							fname, lineno, 0 );
1148						goto fail;
1149					}
1150
1151					/* format of string is
1152						"group/objectClassValue/groupAttrName" */
1153					if ( ( value = strchr(left, '/') ) != NULL ) {
1154						*value++ = '\0';
1155						if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
1156							*name++ = '\0';
1157						}
1158					}
1159
1160					b->a_group_style = sty;
1161					if ( sty == ACL_STYLE_EXPAND ) {
1162						acl_regex_normalized_dn( right, &bv );
1163						if ( !ber_bvccmp( &bv, '*' ) ) {
1164							regtest( fname, lineno, bv.bv_val );
1165						}
1166						b->a_group_pat = bv;
1167
1168					} else {
1169						ber_str2bv( right, 0, 0, &bv );
1170						rc = dnNormalize( 0, NULL, NULL, &bv,
1171							&b->a_group_pat, NULL );
1172						if ( rc != LDAP_SUCCESS ) {
1173							Debug( LDAP_DEBUG_ANY,
1174								"%s: line %d: bad DN \"%s\".\n",
1175								fname, lineno, right );
1176							goto fail;
1177						}
1178					}
1179
1180					if ( value && *value ) {
1181						b->a_group_oc = oc_find( value );
1182						*--value = '/';
1183
1184						if ( b->a_group_oc == NULL ) {
1185							Debug( LDAP_DEBUG_ANY,
1186								"%s: line %d: group objectclass "
1187								"\"%s\" unknown.\n",
1188								fname, lineno, value );
1189							goto fail;
1190						}
1191
1192					} else {
1193						b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );
1194
1195						if( b->a_group_oc == NULL ) {
1196							Debug( LDAP_DEBUG_ANY,
1197								"%s: line %d: group default objectclass "
1198								"\"%s\" unknown.\n",
1199								fname, lineno, SLAPD_GROUP_CLASS );
1200							goto fail;
1201						}
1202					}
1203
1204					if ( is_object_subclass( slap_schema.si_oc_referral,
1205						b->a_group_oc ) )
1206					{
1207						Debug( LDAP_DEBUG_ANY,
1208							"%s: line %d: group objectclass \"%s\" "
1209							"is subclass of referral.\n",
1210							fname, lineno, value );
1211						goto fail;
1212					}
1213
1214					if ( is_object_subclass( slap_schema.si_oc_alias,
1215						b->a_group_oc ) )
1216					{
1217						Debug( LDAP_DEBUG_ANY,
1218							"%s: line %d: group objectclass \"%s\" "
1219							"is subclass of alias.\n",
1220							fname, lineno, value );
1221						goto fail;
1222					}
1223
1224					if ( name && *name ) {
1225						attr_name = name;
1226						*--name = '/';
1227
1228					}
1229
1230					rc = slap_str2ad( attr_name, &b->a_group_at, &text );
1231					if ( rc != LDAP_SUCCESS ) {
1232						char	buf[ SLAP_TEXT_BUFLEN ];
1233
1234						snprintf( buf, sizeof( buf ),
1235							"group \"%s\": %s.",
1236							right, text );
1237						Debug( LDAP_DEBUG_ANY,
1238							"%s: line %d: %s\n",
1239							fname, lineno, buf );
1240						goto fail;
1241					}
1242
1243					if ( !is_at_syntax( b->a_group_at->ad_type,
1244							SLAPD_DN_SYNTAX ) /* e.g. "member" */
1245						&& !is_at_syntax( b->a_group_at->ad_type,
1246							SLAPD_NAMEUID_SYNTAX ) /* e.g. memberUID */
1247						&& !is_at_subtype( b->a_group_at->ad_type,
1248							slap_schema.si_ad_labeledURI->ad_type ) /* e.g. memberURL */ )
1249					{
1250						char	buf[ SLAP_TEXT_BUFLEN ];
1251
1252						snprintf( buf, sizeof( buf ),
1253							"group \"%s\" attr \"%s\": inappropriate syntax: %s; "
1254							"must be " SLAPD_DN_SYNTAX " (DN), "
1255							SLAPD_NAMEUID_SYNTAX " (NameUID) "
1256							"or a subtype of labeledURI.",
1257							right,
1258							attr_name,
1259							at_syntax( b->a_group_at->ad_type ) );
1260						Debug( LDAP_DEBUG_ANY,
1261							"%s: line %d: %s\n",
1262							fname, lineno, buf );
1263						goto fail;
1264					}
1265
1266
1267					{
1268						int rc;
1269						ObjectClass *ocs[2];
1270
1271						ocs[0] = b->a_group_oc;
1272						ocs[1] = NULL;
1273
1274						rc = oc_check_allowed( b->a_group_at->ad_type,
1275							ocs, NULL );
1276
1277						if( rc != 0 ) {
1278							char	buf[ SLAP_TEXT_BUFLEN ];
1279
1280							snprintf( buf, sizeof( buf ),
1281								"group: \"%s\" not allowed by \"%s\".",
1282								b->a_group_at->ad_cname.bv_val,
1283								b->a_group_oc->soc_oid );
1284							Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
1285								fname, lineno, buf );
1286							goto fail;
1287						}
1288					}
1289					continue;
1290				}
1291
1292				if ( strcasecmp( left, "peername" ) == 0 ) {
1293					switch ( sty ) {
1294					case ACL_STYLE_REGEX:
1295					case ACL_STYLE_BASE:
1296						/* legal, traditional */
1297					case ACL_STYLE_EXPAND:
1298						/* cheap replacement to regex for simple expansion */
1299					case ACL_STYLE_IP:
1300					case ACL_STYLE_IPV6:
1301					case ACL_STYLE_PATH:
1302						/* legal, peername specific */
1303						break;
1304
1305					default:
1306						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1307							"inappropriate style \"%s\" in by clause.\n",
1308						    fname, lineno, style );
1309						goto fail;
1310					}
1311
1312					if ( right == NULL || right[0] == '\0' ) {
1313						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1314							"missing \"=\" in (or value after) \"%s\" "
1315							"in by clause.\n",
1316							fname, lineno, left );
1317						goto fail;
1318					}
1319
1320					if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1321						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1322							"peername pattern already specified.\n",
1323							fname, lineno, 0 );
1324						goto fail;
1325					}
1326
1327					b->a_peername_style = sty;
1328					if ( sty == ACL_STYLE_REGEX ) {
1329						acl_regex_normalized_dn( right, &bv );
1330						if ( !ber_bvccmp( &bv, '*' ) ) {
1331							regtest( fname, lineno, bv.bv_val );
1332						}
1333						b->a_peername_pat = bv;
1334
1335					} else {
1336						ber_str2bv( right, 0, 1, &b->a_peername_pat );
1337
1338						if ( sty == ACL_STYLE_IP ) {
1339							char		*addr = NULL,
1340									*mask = NULL,
1341									*port = NULL;
1342
1343							split( right, '{', &addr, &port );
1344							split( addr, '%', &addr, &mask );
1345
1346							b->a_peername_addr = inet_addr( addr );
1347							if ( b->a_peername_addr == (unsigned long)(-1) ) {
1348								/* illegal address */
1349								Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1350									"illegal peername address \"%s\".\n",
1351									fname, lineno, addr );
1352								goto fail;
1353							}
1354
1355							b->a_peername_mask = (unsigned long)(-1);
1356							if ( mask != NULL ) {
1357								b->a_peername_mask = inet_addr( mask );
1358								if ( b->a_peername_mask ==
1359									(unsigned long)(-1) )
1360								{
1361									/* illegal mask */
1362									Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1363										"illegal peername address mask "
1364										"\"%s\".\n",
1365										fname, lineno, mask );
1366									goto fail;
1367								}
1368							}
1369
1370							b->a_peername_port = -1;
1371							if ( port ) {
1372								char	*end = NULL;
1373
1374								b->a_peername_port = strtol( port, &end, 10 );
1375								if ( end == port || end[0] != '}' ) {
1376									/* illegal port */
1377									Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1378										"illegal peername port specification "
1379										"\"{%s}\".\n",
1380										fname, lineno, port );
1381									goto fail;
1382								}
1383							}
1384
1385#ifdef LDAP_PF_INET6
1386						} else if ( sty == ACL_STYLE_IPV6 ) {
1387							char		*addr = NULL,
1388									*mask = NULL,
1389									*port = NULL;
1390
1391							split( right, '{', &addr, &port );
1392							split( addr, '%', &addr, &mask );
1393
1394							if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) {
1395								/* illegal address */
1396								Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1397									"illegal peername address \"%s\".\n",
1398									fname, lineno, addr );
1399								goto fail;
1400							}
1401
1402							if ( mask == NULL ) {
1403								mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
1404							}
1405
1406							if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) {
1407								/* illegal mask */
1408								Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1409									"illegal peername address mask "
1410									"\"%s\".\n",
1411									fname, lineno, mask );
1412								goto fail;
1413							}
1414
1415							b->a_peername_port = -1;
1416							if ( port ) {
1417								char	*end = NULL;
1418
1419								b->a_peername_port = strtol( port, &end, 10 );
1420								if ( end == port || end[0] != '}' ) {
1421									/* illegal port */
1422									Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1423										"illegal peername port specification "
1424										"\"{%s}\".\n",
1425										fname, lineno, port );
1426									goto fail;
1427								}
1428							}
1429#endif /* LDAP_PF_INET6 */
1430						}
1431					}
1432					continue;
1433				}
1434
1435				if ( strcasecmp( left, "sockname" ) == 0 ) {
1436					switch ( sty ) {
1437					case ACL_STYLE_REGEX:
1438					case ACL_STYLE_BASE:
1439						/* legal, traditional */
1440					case ACL_STYLE_EXPAND:
1441						/* cheap replacement to regex for simple expansion */
1442						break;
1443
1444					default:
1445						/* unknown */
1446						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1447							"inappropriate style \"%s\" in by clause\n",
1448						    fname, lineno, style );
1449						goto fail;
1450					}
1451
1452					if ( right == NULL || right[0] == '\0' ) {
1453						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1454							"missing \"=\" in (or value after) \"%s\" "
1455							"in by clause\n",
1456							fname, lineno, left );
1457						goto fail;
1458					}
1459
1460					if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
1461						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1462							"sockname pattern already specified.\n",
1463							fname, lineno, 0 );
1464						goto fail;
1465					}
1466
1467					b->a_sockname_style = sty;
1468					if ( sty == ACL_STYLE_REGEX ) {
1469						acl_regex_normalized_dn( right, &bv );
1470						if ( !ber_bvccmp( &bv, '*' ) ) {
1471							regtest( fname, lineno, bv.bv_val );
1472						}
1473						b->a_sockname_pat = bv;
1474
1475					} else {
1476						ber_str2bv( right, 0, 1, &b->a_sockname_pat );
1477					}
1478					continue;
1479				}
1480
1481				if ( strcasecmp( left, "domain" ) == 0 ) {
1482					switch ( sty ) {
1483					case ACL_STYLE_REGEX:
1484					case ACL_STYLE_BASE:
1485					case ACL_STYLE_SUBTREE:
1486						/* legal, traditional */
1487						break;
1488
1489					case ACL_STYLE_EXPAND:
1490						/* tolerated: means exact,expand */
1491						if ( expand ) {
1492							Debug( LDAP_DEBUG_ANY,
1493								"%s: line %d: "
1494								"\"expand\" modifier "
1495								"with \"expand\" style.\n",
1496								fname, lineno, 0 );
1497						}
1498						sty = ACL_STYLE_BASE;
1499						expand = 1;
1500						break;
1501
1502					default:
1503						/* unknown */
1504						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1505							"inappropriate style \"%s\" in by clause.\n",
1506						    fname, lineno, style );
1507						goto fail;
1508					}
1509
1510					if ( right == NULL || right[0] == '\0' ) {
1511						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1512							"missing \"=\" in (or value after) \"%s\" "
1513							"in by clause.\n",
1514							fname, lineno, left );
1515						goto fail;
1516					}
1517
1518					if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1519						Debug( LDAP_DEBUG_ANY,
1520							"%s: line %d: domain pattern already specified.\n",
1521							fname, lineno, 0 );
1522						goto fail;
1523					}
1524
1525					b->a_domain_style = sty;
1526					b->a_domain_expand = expand;
1527					if ( sty == ACL_STYLE_REGEX ) {
1528						acl_regex_normalized_dn( right, &bv );
1529						if ( !ber_bvccmp( &bv, '*' ) ) {
1530							regtest( fname, lineno, bv.bv_val );
1531						}
1532						b->a_domain_pat = bv;
1533
1534					} else {
1535						ber_str2bv( right, 0, 1, &b->a_domain_pat );
1536					}
1537					continue;
1538				}
1539
1540				if ( strcasecmp( left, "sockurl" ) == 0 ) {
1541					switch ( sty ) {
1542					case ACL_STYLE_REGEX:
1543					case ACL_STYLE_BASE:
1544						/* legal, traditional */
1545					case ACL_STYLE_EXPAND:
1546						/* cheap replacement to regex for simple expansion */
1547						break;
1548
1549					default:
1550						/* unknown */
1551						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1552							"inappropriate style \"%s\" in by clause.\n",
1553						    fname, lineno, style );
1554						goto fail;
1555					}
1556
1557					if ( right == NULL || right[0] == '\0' ) {
1558						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1559							"missing \"=\" in (or value after) \"%s\" "
1560							"in by clause.\n",
1561							fname, lineno, left );
1562						goto fail;
1563					}
1564
1565					if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1566						Debug( LDAP_DEBUG_ANY,
1567							"%s: line %d: sockurl pattern already specified.\n",
1568							fname, lineno, 0 );
1569						goto fail;
1570					}
1571
1572					b->a_sockurl_style = sty;
1573					if ( sty == ACL_STYLE_REGEX ) {
1574						acl_regex_normalized_dn( right, &bv );
1575						if ( !ber_bvccmp( &bv, '*' ) ) {
1576							regtest( fname, lineno, bv.bv_val );
1577						}
1578						b->a_sockurl_pat = bv;
1579
1580					} else {
1581						ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
1582					}
1583					continue;
1584				}
1585
1586				if ( strcasecmp( left, "set" ) == 0 ) {
1587					switch ( sty ) {
1588						/* deprecated */
1589					case ACL_STYLE_REGEX:
1590						Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
1591							"%s: line %d: "
1592							"deprecated set style "
1593							"\"regex\" in <by> clause; "
1594							"use \"expand\" instead.\n",
1595							fname, lineno, 0 );
1596						sty = ACL_STYLE_EXPAND;
1597						/* FALLTHRU */
1598
1599					case ACL_STYLE_BASE:
1600					case ACL_STYLE_EXPAND:
1601						break;
1602
1603					default:
1604						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1605							"inappropriate style \"%s\" in by clause.\n",
1606							fname, lineno, style );
1607						goto fail;
1608					}
1609
1610					if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1611						Debug( LDAP_DEBUG_ANY,
1612							"%s: line %d: set attribute already specified.\n",
1613							fname, lineno, 0 );
1614						goto fail;
1615					}
1616
1617					if ( right == NULL || *right == '\0' ) {
1618						Debug( LDAP_DEBUG_ANY,
1619							"%s: line %d: no set is defined.\n",
1620							fname, lineno, 0 );
1621						goto fail;
1622					}
1623
1624					b->a_set_style = sty;
1625					ber_str2bv( right, 0, 1, &b->a_set_pat );
1626
1627					continue;
1628				}
1629
1630#ifdef SLAP_DYNACL
1631				{
1632					char		*name = NULL,
1633							*opts = NULL;
1634
1635#if 1 /* tolerate legacy "aci" <who> */
1636					if ( strcasecmp( left, "aci" ) == 0 ) {
1637						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1638							"undocumented deprecated \"aci\" directive "
1639							"is superseded by \"dynacl/aci\".\n",
1640							fname, lineno, 0 );
1641						name = "aci";
1642
1643					} else
1644#endif /* tolerate legacy "aci" <who> */
1645					if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
1646						name = &left[ STRLENOF( "dynacl/" ) ];
1647						opts = strchr( name, '/' );
1648						if ( opts ) {
1649							opts[ 0 ] = '\0';
1650							opts++;
1651						}
1652					}
1653
1654					if ( name ) {
1655						if ( slap_dynacl_config( fname, lineno, b, name, opts, sty, right ) ) {
1656							Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1657								"unable to configure dynacl \"%s\".\n",
1658								fname, lineno, name );
1659							goto fail;
1660						}
1661
1662						continue;
1663					}
1664				}
1665#endif /* SLAP_DYNACL */
1666
1667				if ( strcasecmp( left, "ssf" ) == 0 ) {
1668					if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1669						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1670							"inappropriate style \"%s\" in by clause.\n",
1671						    fname, lineno, style );
1672						goto fail;
1673					}
1674
1675					if ( b->a_authz.sai_ssf ) {
1676						Debug( LDAP_DEBUG_ANY,
1677							"%s: line %d: ssf attribute already specified.\n",
1678							fname, lineno, 0 );
1679						goto fail;
1680					}
1681
1682					if ( right == NULL || *right == '\0' ) {
1683						Debug( LDAP_DEBUG_ANY,
1684							"%s: line %d: no ssf is defined.\n",
1685							fname, lineno, 0 );
1686						goto fail;
1687					}
1688
1689					if ( lutil_atou( &b->a_authz.sai_ssf, right ) != 0 ) {
1690						Debug( LDAP_DEBUG_ANY,
1691							"%s: line %d: unable to parse ssf value (%s).\n",
1692							fname, lineno, right );
1693						goto fail;
1694					}
1695
1696					if ( !b->a_authz.sai_ssf ) {
1697						Debug( LDAP_DEBUG_ANY,
1698							"%s: line %d: invalid ssf value (%s).\n",
1699							fname, lineno, right );
1700						goto fail;
1701					}
1702					continue;
1703				}
1704
1705				if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
1706					if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1707						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1708							"inappropriate style \"%s\" in by clause.\n",
1709							fname, lineno, style );
1710						goto fail;
1711					}
1712
1713					if ( b->a_authz.sai_transport_ssf ) {
1714						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1715							"transport_ssf attribute already specified.\n",
1716							fname, lineno, 0 );
1717						goto fail;
1718					}
1719
1720					if ( right == NULL || *right == '\0' ) {
1721						Debug( LDAP_DEBUG_ANY,
1722							"%s: line %d: no transport_ssf is defined.\n",
1723							fname, lineno, 0 );
1724						goto fail;
1725					}
1726
1727					if ( lutil_atou( &b->a_authz.sai_transport_ssf, right ) != 0 ) {
1728						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1729							"unable to parse transport_ssf value (%s).\n",
1730							fname, lineno, right );
1731						goto fail;
1732					}
1733
1734					if ( !b->a_authz.sai_transport_ssf ) {
1735						Debug( LDAP_DEBUG_ANY,
1736							"%s: line %d: invalid transport_ssf value (%s).\n",
1737							fname, lineno, right );
1738						goto fail;
1739					}
1740					continue;
1741				}
1742
1743				if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
1744					if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1745						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1746							"inappropriate style \"%s\" in by clause.\n",
1747							fname, lineno, style );
1748						goto fail;
1749					}
1750
1751					if ( b->a_authz.sai_tls_ssf ) {
1752						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1753							"tls_ssf attribute already specified.\n",
1754							fname, lineno, 0 );
1755						goto fail;
1756					}
1757
1758					if ( right == NULL || *right == '\0' ) {
1759						Debug( LDAP_DEBUG_ANY,
1760							"%s: line %d: no tls_ssf is defined\n",
1761							fname, lineno, 0 );
1762						goto fail;
1763					}
1764
1765					if ( lutil_atou( &b->a_authz.sai_tls_ssf, right ) != 0 ) {
1766						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1767							"unable to parse tls_ssf value (%s).\n",
1768							fname, lineno, right );
1769						goto fail;
1770					}
1771
1772					if ( !b->a_authz.sai_tls_ssf ) {
1773						Debug( LDAP_DEBUG_ANY,
1774							"%s: line %d: invalid tls_ssf value (%s).\n",
1775							fname, lineno, right );
1776						goto fail;
1777					}
1778					continue;
1779				}
1780
1781				if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1782					if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1783						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1784							"inappropriate style \"%s\" in by clause.\n",
1785							fname, lineno, style );
1786						goto fail;
1787					}
1788
1789					if ( b->a_authz.sai_sasl_ssf ) {
1790						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1791							"sasl_ssf attribute already specified.\n",
1792							fname, lineno, 0 );
1793						goto fail;
1794					}
1795
1796					if ( right == NULL || *right == '\0' ) {
1797						Debug( LDAP_DEBUG_ANY,
1798							"%s: line %d: no sasl_ssf is defined.\n",
1799							fname, lineno, 0 );
1800						goto fail;
1801					}
1802
1803					if ( lutil_atou( &b->a_authz.sai_sasl_ssf, right ) != 0 ) {
1804						Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1805							"unable to parse sasl_ssf value (%s).\n",
1806							fname, lineno, right );
1807						goto fail;
1808					}
1809
1810					if ( !b->a_authz.sai_sasl_ssf ) {
1811						Debug( LDAP_DEBUG_ANY,
1812							"%s: line %d: invalid sasl_ssf value (%s).\n",
1813							fname, lineno, right );
1814						goto fail;
1815					}
1816					continue;
1817				}
1818
1819				if ( right != NULL ) {
1820					/* unsplit */
1821					right[-1] = '=';
1822				}
1823				break;
1824			}
1825
1826			if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) {
1827				/* out of arguments or plain stop */
1828
1829				ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1830				ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1831				b->a_type = ACL_STOP;
1832
1833				access_append( &a->acl_access, b );
1834				continue;
1835			}
1836
1837			if ( strcasecmp( left, "continue" ) == 0 ) {
1838				/* plain continue */
1839
1840				ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1841				ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1842				b->a_type = ACL_CONTINUE;
1843
1844				access_append( &a->acl_access, b );
1845				continue;
1846			}
1847
1848			if ( strcasecmp( left, "break" ) == 0 ) {
1849				/* plain continue */
1850
1851				ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1852				ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1853				b->a_type = ACL_BREAK;
1854
1855				access_append( &a->acl_access, b );
1856				continue;
1857			}
1858
1859			if ( strcasecmp( left, "by" ) == 0 ) {
1860				/* we've gone too far */
1861				--i;
1862				ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1863				ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1864				b->a_type = ACL_STOP;
1865
1866				access_append( &a->acl_access, b );
1867				continue;
1868			}
1869
1870			/* get <access> */
1871			{
1872				char	*lleft = left;
1873
1874				if ( strncasecmp( left, "self", STRLENOF( "self" ) ) == 0 ) {
1875					b->a_dn_self = 1;
1876					lleft = &left[ STRLENOF( "self" ) ];
1877
1878				} else if ( strncasecmp( left, "realself", STRLENOF( "realself" ) ) == 0 ) {
1879					b->a_realdn_self = 1;
1880					lleft = &left[ STRLENOF( "realself" ) ];
1881				}
1882
1883				ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( lleft ) );
1884			}
1885
1886			if ( ACL_IS_INVALID( b->a_access_mask ) ) {
1887				Debug( LDAP_DEBUG_ANY,
1888					"%s: line %d: expecting <access> got \"%s\".\n",
1889					fname, lineno, left );
1890				goto fail;
1891			}
1892
1893			b->a_type = ACL_STOP;
1894
1895			if ( ++i == argc ) {
1896				/* out of arguments or plain stop */
1897				access_append( &a->acl_access, b );
1898				continue;
1899			}
1900
1901			if ( strcasecmp( argv[i], "continue" ) == 0 ) {
1902				/* plain continue */
1903				b->a_type = ACL_CONTINUE;
1904
1905			} else if ( strcasecmp( argv[i], "break" ) == 0 ) {
1906				/* plain continue */
1907				b->a_type = ACL_BREAK;
1908
1909			} else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1910				/* gone to far */
1911				i--;
1912			}
1913
1914			access_append( &a->acl_access, b );
1915			b = NULL;
1916
1917		} else {
1918			Debug( LDAP_DEBUG_ANY,
1919				"%s: line %d: expecting \"to\" "
1920				"or \"by\" got \"%s\"\n",
1921				fname, lineno, argv[i] );
1922			goto fail;
1923		}
1924	}
1925
1926	/* if we have no real access clause, complain and do nothing */
1927	if ( a == NULL ) {
1928		Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1929			"warning: no access clause(s) specified in access line.\n",
1930			fname, lineno, 0 );
1931		goto fail;
1932
1933	} else {
1934#ifdef LDAP_DEBUG
1935		if ( slap_debug & LDAP_DEBUG_ACL ) {
1936			print_acl( be, a );
1937		}
1938#endif
1939
1940		if ( a->acl_access == NULL ) {
1941			Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1942				"warning: no by clause(s) specified in access line.\n",
1943				fname, lineno, 0 );
1944			goto fail;
1945		}
1946
1947		if ( be != NULL ) {
1948			if ( be->be_nsuffix == NULL ) {
1949				Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1950					"scope checking needs suffix before ACLs.\n",
1951					fname, lineno, 0 );
1952				/* go ahead, since checking is not authoritative */
1953			} else if ( !BER_BVISNULL( &be->be_nsuffix[ 1 ] ) ) {
1954				Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1955					"scope checking only applies to single-valued "
1956					"suffix databases\n",
1957					fname, lineno, 0 );
1958				/* go ahead, since checking is not authoritative */
1959			} else {
1960				switch ( check_scope( be, a ) ) {
1961				case ACL_SCOPE_UNKNOWN:
1962					Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1963						"cannot assess the validity of the ACL scope within "
1964						"backend naming context\n",
1965						fname, lineno, 0 );
1966					break;
1967
1968				case ACL_SCOPE_WARN:
1969					Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1970						"ACL could be out of scope within backend naming context\n",
1971						fname, lineno, 0 );
1972					break;
1973
1974				case ACL_SCOPE_PARTIAL:
1975					Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1976						"ACL appears to be partially out of scope within "
1977						"backend naming context\n",
1978						fname, lineno, 0 );
1979					break;
1980
1981				case ACL_SCOPE_ERR:
1982					Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1983						"ACL appears to be out of scope within "
1984						"backend naming context\n",
1985						fname, lineno, 0 );
1986					break;
1987
1988				default:
1989					break;
1990				}
1991			}
1992			acl_append( &be->be_acl, a, pos );
1993
1994		} else {
1995			acl_append( &frontendDB->be_acl, a, pos );
1996		}
1997	}
1998
1999	return 0;
2000
2001fail:
2002	if ( b ) access_free( b );
2003	if ( a ) acl_free( a );
2004	return acl_usage();
2005}
2006
2007char *
2008accessmask2str( slap_mask_t mask, char *buf, int debug )
2009{
2010	int	none = 1;
2011	char	*ptr = buf;
2012
2013	assert( buf != NULL );
2014
2015	if ( ACL_IS_INVALID( mask ) ) {
2016		return "invalid";
2017	}
2018
2019	buf[0] = '\0';
2020
2021	if ( ACL_IS_LEVEL( mask ) ) {
2022		if ( ACL_LVL_IS_NONE(mask) ) {
2023			ptr = lutil_strcopy( ptr, "none" );
2024
2025		} else if ( ACL_LVL_IS_DISCLOSE(mask) ) {
2026			ptr = lutil_strcopy( ptr, "disclose" );
2027
2028		} else if ( ACL_LVL_IS_AUTH(mask) ) {
2029			ptr = lutil_strcopy( ptr, "auth" );
2030
2031		} else if ( ACL_LVL_IS_COMPARE(mask) ) {
2032			ptr = lutil_strcopy( ptr, "compare" );
2033
2034		} else if ( ACL_LVL_IS_SEARCH(mask) ) {
2035			ptr = lutil_strcopy( ptr, "search" );
2036
2037		} else if ( ACL_LVL_IS_READ(mask) ) {
2038			ptr = lutil_strcopy( ptr, "read" );
2039
2040		} else if ( ACL_LVL_IS_WRITE(mask) ) {
2041			ptr = lutil_strcopy( ptr, "write" );
2042
2043		} else if ( ACL_LVL_IS_WADD(mask) ) {
2044			ptr = lutil_strcopy( ptr, "add" );
2045
2046		} else if ( ACL_LVL_IS_WDEL(mask) ) {
2047			ptr = lutil_strcopy( ptr, "delete" );
2048
2049		} else if ( ACL_LVL_IS_MANAGE(mask) ) {
2050			ptr = lutil_strcopy( ptr, "manage" );
2051
2052		} else {
2053			ptr = lutil_strcopy( ptr, "unknown" );
2054		}
2055
2056		if ( !debug ) {
2057			*ptr = '\0';
2058			return buf;
2059		}
2060		*ptr++ = '(';
2061	}
2062
2063	if( ACL_IS_ADDITIVE( mask ) ) {
2064		*ptr++ = '+';
2065
2066	} else if( ACL_IS_SUBTRACTIVE( mask ) ) {
2067		*ptr++ = '-';
2068
2069	} else {
2070		*ptr++ = '=';
2071	}
2072
2073	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_MANAGE) ) {
2074		none = 0;
2075		*ptr++ = 'm';
2076	}
2077
2078	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
2079		none = 0;
2080		*ptr++ = 'w';
2081
2082	} else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WADD) ) {
2083		none = 0;
2084		*ptr++ = 'a';
2085
2086	} else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) {
2087		none = 0;
2088		*ptr++ = 'z';
2089	}
2090
2091	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
2092		none = 0;
2093		*ptr++ = 'r';
2094	}
2095
2096	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
2097		none = 0;
2098		*ptr++ = 's';
2099	}
2100
2101	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
2102		none = 0;
2103		*ptr++ = 'c';
2104	}
2105
2106	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
2107		none = 0;
2108		*ptr++ = 'x';
2109	}
2110
2111	if ( ACL_PRIV_ISSET(mask, ACL_PRIV_DISCLOSE) ) {
2112		none = 0;
2113		*ptr++ = 'd';
2114	}
2115
2116	if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
2117		none = 0;
2118		*ptr++ = '0';
2119	}
2120
2121	if ( none ) {
2122		ptr = buf;
2123	}
2124
2125	if ( ACL_IS_LEVEL( mask ) ) {
2126		*ptr++ = ')';
2127	}
2128
2129	*ptr = '\0';
2130
2131	return buf;
2132}
2133
2134slap_mask_t
2135str2accessmask( const char *str )
2136{
2137	slap_mask_t	mask;
2138
2139	if( !ASCII_ALPHA(str[0]) ) {
2140		int i;
2141
2142		if ( str[0] == '=' ) {
2143			ACL_INIT(mask);
2144
2145		} else if( str[0] == '+' ) {
2146			ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
2147
2148		} else if( str[0] == '-' ) {
2149			ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
2150
2151		} else {
2152			ACL_INVALIDATE(mask);
2153			return mask;
2154		}
2155
2156		for( i=1; str[i] != '\0'; i++ ) {
2157			if( TOLOWER((unsigned char) str[i]) == 'm' ) {
2158				ACL_PRIV_SET(mask, ACL_PRIV_MANAGE);
2159
2160			} else if( TOLOWER((unsigned char) str[i]) == 'w' ) {
2161				ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
2162
2163			} else if( TOLOWER((unsigned char) str[i]) == 'a' ) {
2164				ACL_PRIV_SET(mask, ACL_PRIV_WADD);
2165
2166			} else if( TOLOWER((unsigned char) str[i]) == 'z' ) {
2167				ACL_PRIV_SET(mask, ACL_PRIV_WDEL);
2168
2169			} else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
2170				ACL_PRIV_SET(mask, ACL_PRIV_READ);
2171
2172			} else if( TOLOWER((unsigned char) str[i]) == 's' ) {
2173				ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
2174
2175			} else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
2176				ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
2177
2178			} else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
2179				ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
2180
2181			} else if( TOLOWER((unsigned char) str[i]) == 'd' ) {
2182				ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
2183
2184			} else if( str[i] == '0' ) {
2185				ACL_PRIV_SET(mask, ACL_PRIV_NONE);
2186
2187			} else {
2188				ACL_INVALIDATE(mask);
2189				return mask;
2190			}
2191		}
2192
2193		return mask;
2194	}
2195
2196	if ( strcasecmp( str, "none" ) == 0 ) {
2197		ACL_LVL_ASSIGN_NONE(mask);
2198
2199	} else if ( strcasecmp( str, "disclose" ) == 0 ) {
2200		ACL_LVL_ASSIGN_DISCLOSE(mask);
2201
2202	} else if ( strcasecmp( str, "auth" ) == 0 ) {
2203		ACL_LVL_ASSIGN_AUTH(mask);
2204
2205	} else if ( strcasecmp( str, "compare" ) == 0 ) {
2206		ACL_LVL_ASSIGN_COMPARE(mask);
2207
2208	} else if ( strcasecmp( str, "search" ) == 0 ) {
2209		ACL_LVL_ASSIGN_SEARCH(mask);
2210
2211	} else if ( strcasecmp( str, "read" ) == 0 ) {
2212		ACL_LVL_ASSIGN_READ(mask);
2213
2214	} else if ( strcasecmp( str, "add" ) == 0 ) {
2215		ACL_LVL_ASSIGN_WADD(mask);
2216
2217	} else if ( strcasecmp( str, "delete" ) == 0 ) {
2218		ACL_LVL_ASSIGN_WDEL(mask);
2219
2220	} else if ( strcasecmp( str, "write" ) == 0 ) {
2221		ACL_LVL_ASSIGN_WRITE(mask);
2222
2223	} else if ( strcasecmp( str, "manage" ) == 0 ) {
2224		ACL_LVL_ASSIGN_MANAGE(mask);
2225
2226	} else {
2227		ACL_INVALIDATE( mask );
2228	}
2229
2230	return mask;
2231}
2232
2233static int
2234acl_usage( void )
2235{
2236	char *access =
2237		"<access clause> ::= access to <what> "
2238				"[ by <who> [ <access> ] [ <control> ] ]+ \n";
2239	char *what =
2240		"<what> ::= * | dn[.<dnstyle>=<DN>] [filter=<filter>] [attrs=<attrspec>]\n"
2241		"<attrspec> ::= <attrname> [val[/<matchingRule>][.<attrstyle>]=<value>] | <attrlist>\n"
2242		"<attrlist> ::= <attr> [ , <attrlist> ]\n"
2243		"<attr> ::= <attrname> | @<objectClass> | !<objectClass> | entry | children\n";
2244
2245	char *who =
2246		"<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
2247			"\t[ realanonymous | realusers | realself | realdn[.<dnstyle>]=<DN> ]\n"
2248			"\t[dnattr=<attrname>]\n"
2249			"\t[realdnattr=<attrname>]\n"
2250			"\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
2251			"\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n"
2252			"\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
2253#ifdef SLAP_DYNACL
2254			"\t[dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]]\n"
2255#endif /* SLAP_DYNACL */
2256			"\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
2257		"<style> ::= exact | regex | base(Object)\n"
2258		"<dnstyle> ::= base(Object) | one(level) | sub(tree) | children | "
2259			"exact | regex\n"
2260		"<attrstyle> ::= exact | regex | base(Object) | one(level) | "
2261			"sub(tree) | children\n"
2262		"<peernamestyle> ::= exact | regex | ip | ipv6 | path\n"
2263		"<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n"
2264		"<access> ::= [[real]self]{<level>|<priv>}\n"
2265		"<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n"
2266		"<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n"
2267		"<control> ::= [ stop | continue | break ]\n"
2268#ifdef SLAP_DYNACL
2269#ifdef SLAPD_ACI_ENABLED
2270		"dynacl:\n"
2271		"\t<name>=ACI\t<pattern>=<attrname>\n"
2272#endif /* SLAPD_ACI_ENABLED */
2273#endif /* ! SLAP_DYNACL */
2274		"";
2275
2276	Debug( LDAP_DEBUG_ANY, "%s%s%s\n", access, what, who );
2277
2278	return 1;
2279}
2280
2281/*
2282 * Set pattern to a "normalized" DN from src.
2283 * At present it simply eats the (optional) space after
2284 * a RDN separator (,)
2285 * Eventually will evolve in a more complete normalization
2286 */
2287static void
2288acl_regex_normalized_dn(
2289	const char *src,
2290	struct berval *pattern )
2291{
2292	char *str, *p;
2293	ber_len_t len;
2294
2295	str = ch_strdup( src );
2296	len = strlen( src );
2297
2298	for ( p = str; p && p[0]; p++ ) {
2299		/* escape */
2300		if ( p[0] == '\\' && p[1] ) {
2301			/*
2302			 * if escaping a hex pair we should
2303			 * increment p twice; however, in that
2304			 * case the second hex number does
2305			 * no harm
2306			 */
2307			p++;
2308		}
2309
2310		if ( p[0] == ',' && p[1] == ' ' ) {
2311			char *q;
2312
2313			/*
2314			 * too much space should be an error if we are pedantic
2315			 */
2316			for ( q = &p[2]; q[0] == ' '; q++ ) {
2317				/* DO NOTHING */ ;
2318			}
2319			AC_MEMCPY( p+1, q, len-(q-str)+1);
2320		}
2321	}
2322	pattern->bv_val = str;
2323	pattern->bv_len = p - str;
2324
2325	return;
2326}
2327
2328static void
2329split(
2330    char	*line,
2331    int		splitchar,
2332    char	**left,
2333    char	**right )
2334{
2335	*left = line;
2336	if ( (*right = strchr( line, splitchar )) != NULL ) {
2337		*((*right)++) = '\0';
2338	}
2339}
2340
2341static void
2342access_append( Access **l, Access *a )
2343{
2344	for ( ; *l != NULL; l = &(*l)->a_next ) {
2345		;	/* Empty */
2346	}
2347
2348	*l = a;
2349}
2350
2351void
2352acl_append( AccessControl **l, AccessControl *a, int pos )
2353{
2354	int i;
2355
2356	for (i=0 ; i != pos && *l != NULL; l = &(*l)->acl_next, i++ ) {
2357		;	/* Empty */
2358	}
2359	if ( *l && a )
2360		a->acl_next = *l;
2361	*l = a;
2362}
2363
2364static void
2365access_free( Access *a )
2366{
2367	if ( !BER_BVISNULL( &a->a_dn_pat ) ) {
2368		free( a->a_dn_pat.bv_val );
2369	}
2370	if ( !BER_BVISNULL( &a->a_realdn_pat ) ) {
2371		free( a->a_realdn_pat.bv_val );
2372	}
2373	if ( !BER_BVISNULL( &a->a_peername_pat ) ) {
2374		free( a->a_peername_pat.bv_val );
2375	}
2376	if ( !BER_BVISNULL( &a->a_sockname_pat ) ) {
2377		free( a->a_sockname_pat.bv_val );
2378	}
2379	if ( !BER_BVISNULL( &a->a_domain_pat ) ) {
2380		free( a->a_domain_pat.bv_val );
2381	}
2382	if ( !BER_BVISNULL( &a->a_sockurl_pat ) ) {
2383		free( a->a_sockurl_pat.bv_val );
2384	}
2385	if ( !BER_BVISNULL( &a->a_set_pat ) ) {
2386		free( a->a_set_pat.bv_val );
2387	}
2388	if ( !BER_BVISNULL( &a->a_group_pat ) ) {
2389		free( a->a_group_pat.bv_val );
2390	}
2391#ifdef SLAP_DYNACL
2392	if ( a->a_dynacl != NULL ) {
2393		slap_dynacl_t	*da;
2394		for ( da = a->a_dynacl; da; ) {
2395			slap_dynacl_t	*tmp = da;
2396
2397			da = da->da_next;
2398
2399			if ( tmp->da_destroy ) {
2400				tmp->da_destroy( tmp->da_private );
2401			}
2402
2403			ch_free( tmp );
2404		}
2405	}
2406#endif /* SLAP_DYNACL */
2407	free( a );
2408}
2409
2410void
2411acl_free( AccessControl *a )
2412{
2413	Access *n;
2414	AttributeName *an;
2415
2416	if ( a->acl_filter ) {
2417		filter_free( a->acl_filter );
2418	}
2419	if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2420		if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
2421			regfree( &a->acl_dn_re );
2422		}
2423		free ( a->acl_dn_pat.bv_val );
2424	}
2425	if ( a->acl_attrs ) {
2426		for ( an = a->acl_attrs; !BER_BVISNULL( &an->an_name ); an++ ) {
2427			free( an->an_name.bv_val );
2428		}
2429		free( a->acl_attrs );
2430
2431		if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
2432			regfree( &a->acl_attrval_re );
2433		}
2434
2435		if ( !BER_BVISNULL( &a->acl_attrval ) ) {
2436			ber_memfree( a->acl_attrval.bv_val );
2437		}
2438	}
2439	for ( ; a->acl_access; a->acl_access = n ) {
2440		n = a->acl_access->a_next;
2441		access_free( a->acl_access );
2442	}
2443	free( a );
2444}
2445
2446void
2447acl_destroy( AccessControl *a )
2448{
2449	AccessControl *n;
2450
2451	for ( ; a; a = n ) {
2452		n = a->acl_next;
2453		acl_free( a );
2454	}
2455}
2456
2457char *
2458access2str( slap_access_t access )
2459{
2460	if ( access == ACL_NONE ) {
2461		return "none";
2462
2463	} else if ( access == ACL_DISCLOSE ) {
2464		return "disclose";
2465
2466	} else if ( access == ACL_AUTH ) {
2467		return "auth";
2468
2469	} else if ( access == ACL_COMPARE ) {
2470		return "compare";
2471
2472	} else if ( access == ACL_SEARCH ) {
2473		return "search";
2474
2475	} else if ( access == ACL_READ ) {
2476		return "read";
2477
2478	} else if ( access == ACL_WRITE ) {
2479		return "write";
2480
2481	} else if ( access == ACL_WADD ) {
2482		return "add";
2483
2484	} else if ( access == ACL_WDEL ) {
2485		return "delete";
2486
2487	} else if ( access == ACL_MANAGE ) {
2488		return "manage";
2489
2490	}
2491
2492	return "unknown";
2493}
2494
2495slap_access_t
2496str2access( const char *str )
2497{
2498	if ( strcasecmp( str, "none" ) == 0 ) {
2499		return ACL_NONE;
2500
2501	} else if ( strcasecmp( str, "disclose" ) == 0 ) {
2502		return ACL_DISCLOSE;
2503
2504	} else if ( strcasecmp( str, "auth" ) == 0 ) {
2505		return ACL_AUTH;
2506
2507	} else if ( strcasecmp( str, "compare" ) == 0 ) {
2508		return ACL_COMPARE;
2509
2510	} else if ( strcasecmp( str, "search" ) == 0 ) {
2511		return ACL_SEARCH;
2512
2513	} else if ( strcasecmp( str, "read" ) == 0 ) {
2514		return ACL_READ;
2515
2516	} else if ( strcasecmp( str, "write" ) == 0 ) {
2517		return ACL_WRITE;
2518
2519	} else if ( strcasecmp( str, "add" ) == 0 ) {
2520		return ACL_WADD;
2521
2522	} else if ( strcasecmp( str, "delete" ) == 0 ) {
2523		return ACL_WDEL;
2524
2525	} else if ( strcasecmp( str, "manage" ) == 0 ) {
2526		return ACL_MANAGE;
2527	}
2528
2529	return( ACL_INVALID_ACCESS );
2530}
2531
2532#define ACLBUF_MAXLEN	8192
2533
2534static char aclbuf[ACLBUF_MAXLEN];
2535
2536static char *
2537dnaccess2text( slap_dn_access *bdn, char *ptr, int is_realdn )
2538{
2539	*ptr++ = ' ';
2540
2541	if ( is_realdn ) {
2542		ptr = lutil_strcopy( ptr, "real" );
2543	}
2544
2545	if ( ber_bvccmp( &bdn->a_pat, '*' ) ||
2546		bdn->a_style == ACL_STYLE_ANONYMOUS ||
2547		bdn->a_style == ACL_STYLE_USERS ||
2548		bdn->a_style == ACL_STYLE_SELF )
2549	{
2550		if ( is_realdn ) {
2551			assert( ! ber_bvccmp( &bdn->a_pat, '*' ) );
2552		}
2553
2554		ptr = lutil_strcopy( ptr, bdn->a_pat.bv_val );
2555		if ( bdn->a_style == ACL_STYLE_SELF && bdn->a_self_level != 0 ) {
2556			int n = sprintf( ptr, ".level{%d}", bdn->a_self_level );
2557			if ( n > 0 ) {
2558				ptr += n;
2559			} /* else ? */
2560		}
2561
2562	} else {
2563		ptr = lutil_strcopy( ptr, "dn." );
2564		if ( bdn->a_style == ACL_STYLE_BASE )
2565			ptr = lutil_strcopy( ptr, style_base );
2566		else
2567			ptr = lutil_strcopy( ptr, style_strings[bdn->a_style] );
2568		if ( bdn->a_style == ACL_STYLE_LEVEL ) {
2569			int n = sprintf( ptr, "{%d}", bdn->a_level );
2570			if ( n > 0 ) {
2571				ptr += n;
2572			} /* else ? */
2573		}
2574		if ( bdn->a_expand ) {
2575			ptr = lutil_strcopy( ptr, ",expand" );
2576		}
2577		*ptr++ = '=';
2578		*ptr++ = '"';
2579		ptr = lutil_strcopy( ptr, bdn->a_pat.bv_val );
2580		*ptr++ = '"';
2581	}
2582	return ptr;
2583}
2584
2585static char *
2586access2text( Access *b, char *ptr )
2587{
2588	char maskbuf[ACCESSMASK_MAXLEN];
2589
2590	ptr = lutil_strcopy( ptr, "\tby" );
2591
2592	if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
2593		ptr = dnaccess2text( &b->a_dn, ptr, 0 );
2594	}
2595	if ( b->a_dn_at ) {
2596		ptr = lutil_strcopy( ptr, " dnattr=" );
2597		ptr = lutil_strcopy( ptr, b->a_dn_at->ad_cname.bv_val );
2598	}
2599
2600	if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
2601		ptr = dnaccess2text( &b->a_realdn, ptr, 1 );
2602	}
2603	if ( b->a_realdn_at ) {
2604		ptr = lutil_strcopy( ptr, " realdnattr=" );
2605		ptr = lutil_strcopy( ptr, b->a_realdn_at->ad_cname.bv_val );
2606	}
2607
2608	if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
2609		ptr = lutil_strcopy( ptr, " group/" );
2610		ptr = lutil_strcopy( ptr, b->a_group_oc ?
2611			b->a_group_oc->soc_cname.bv_val : SLAPD_GROUP_CLASS );
2612		*ptr++ = '/';
2613		ptr = lutil_strcopy( ptr, b->a_group_at ?
2614			b->a_group_at->ad_cname.bv_val : SLAPD_GROUP_ATTR );
2615		*ptr++ = '.';
2616		ptr = lutil_strcopy( ptr, style_strings[b->a_group_style] );
2617		*ptr++ = '=';
2618		*ptr++ = '"';
2619		ptr = lutil_strcopy( ptr, b->a_group_pat.bv_val );
2620		*ptr++ = '"';
2621	}
2622
2623	if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
2624		ptr = lutil_strcopy( ptr, " peername" );
2625		*ptr++ = '.';
2626		ptr = lutil_strcopy( ptr, style_strings[b->a_peername_style] );
2627		*ptr++ = '=';
2628		*ptr++ = '"';
2629		ptr = lutil_strcopy( ptr, b->a_peername_pat.bv_val );
2630		*ptr++ = '"';
2631	}
2632
2633	if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
2634		ptr = lutil_strcopy( ptr, " sockname" );
2635		*ptr++ = '.';
2636		ptr = lutil_strcopy( ptr, style_strings[b->a_sockname_style] );
2637		*ptr++ = '=';
2638		*ptr++ = '"';
2639		ptr = lutil_strcopy( ptr, b->a_sockname_pat.bv_val );
2640		*ptr++ = '"';
2641	}
2642
2643	if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
2644		ptr = lutil_strcopy( ptr, " domain" );
2645		*ptr++ = '.';
2646		ptr = lutil_strcopy( ptr, style_strings[b->a_domain_style] );
2647		if ( b->a_domain_expand ) {
2648			ptr = lutil_strcopy( ptr, ",expand" );
2649		}
2650		*ptr++ = '=';
2651		ptr = lutil_strcopy( ptr, b->a_domain_pat.bv_val );
2652	}
2653
2654	if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
2655		ptr = lutil_strcopy( ptr, " sockurl" );
2656		*ptr++ = '.';
2657		ptr = lutil_strcopy( ptr, style_strings[b->a_sockurl_style] );
2658		*ptr++ = '=';
2659		*ptr++ = '"';
2660		ptr = lutil_strcopy( ptr, b->a_sockurl_pat.bv_val );
2661		*ptr++ = '"';
2662	}
2663
2664	if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
2665		ptr = lutil_strcopy( ptr, " set" );
2666		*ptr++ = '.';
2667		ptr = lutil_strcopy( ptr, style_strings[b->a_set_style] );
2668		*ptr++ = '=';
2669		*ptr++ = '"';
2670		ptr = lutil_strcopy( ptr, b->a_set_pat.bv_val );
2671		*ptr++ = '"';
2672	}
2673
2674#ifdef SLAP_DYNACL
2675	if ( b->a_dynacl ) {
2676		slap_dynacl_t	*da;
2677
2678		for ( da = b->a_dynacl; da; da = da->da_next ) {
2679			if ( da->da_unparse ) {
2680				struct berval bv = BER_BVNULL;
2681				(void)( *da->da_unparse )( da->da_private, &bv );
2682				assert( !BER_BVISNULL( &bv ) );
2683				ptr = lutil_strcopy( ptr, bv.bv_val );
2684				ch_free( bv.bv_val );
2685			}
2686		}
2687	}
2688#endif /* SLAP_DYNACL */
2689
2690	/* Security Strength Factors */
2691	if ( b->a_authz.sai_ssf ) {
2692		ptr += sprintf( ptr, " ssf=%u",
2693			b->a_authz.sai_ssf );
2694	}
2695	if ( b->a_authz.sai_transport_ssf ) {
2696		ptr += sprintf( ptr, " transport_ssf=%u",
2697			b->a_authz.sai_transport_ssf );
2698	}
2699	if ( b->a_authz.sai_tls_ssf ) {
2700		ptr += sprintf( ptr, " tls_ssf=%u",
2701			b->a_authz.sai_tls_ssf );
2702	}
2703	if ( b->a_authz.sai_sasl_ssf ) {
2704		ptr += sprintf( ptr, " sasl_ssf=%u",
2705			b->a_authz.sai_sasl_ssf );
2706	}
2707
2708	*ptr++ = ' ';
2709	if ( b->a_dn_self ) {
2710		ptr = lutil_strcopy( ptr, "self" );
2711	} else if ( b->a_realdn_self ) {
2712		ptr = lutil_strcopy( ptr, "realself" );
2713	}
2714	ptr = lutil_strcopy( ptr, accessmask2str( b->a_access_mask, maskbuf, 0 ));
2715	if ( !maskbuf[0] ) ptr--;
2716
2717	if( b->a_type == ACL_BREAK ) {
2718		ptr = lutil_strcopy( ptr, " break" );
2719
2720	} else if( b->a_type == ACL_CONTINUE ) {
2721		ptr = lutil_strcopy( ptr, " continue" );
2722
2723	} else if( b->a_type != ACL_STOP ) {
2724		ptr = lutil_strcopy( ptr, " unknown-control" );
2725	} else {
2726		if ( !maskbuf[0] ) ptr = lutil_strcopy( ptr, " stop" );
2727	}
2728	*ptr++ = '\n';
2729
2730	return ptr;
2731}
2732
2733void
2734acl_unparse( AccessControl *a, struct berval *bv )
2735{
2736	Access	*b;
2737	char	*ptr;
2738	int	to = 0;
2739
2740	bv->bv_val = aclbuf;
2741	bv->bv_len = 0;
2742
2743	ptr = bv->bv_val;
2744
2745	ptr = lutil_strcopy( ptr, "to" );
2746	if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2747		to++;
2748		ptr = lutil_strcopy( ptr, " dn." );
2749		if ( a->acl_dn_style == ACL_STYLE_BASE )
2750			ptr = lutil_strcopy( ptr, style_base );
2751		else
2752			ptr = lutil_strcopy( ptr, style_strings[a->acl_dn_style] );
2753		*ptr++ = '=';
2754		*ptr++ = '"';
2755		ptr = lutil_strcopy( ptr, a->acl_dn_pat.bv_val );
2756		ptr = lutil_strcopy( ptr, "\"\n" );
2757	}
2758
2759	if ( a->acl_filter != NULL ) {
2760		struct berval	bv = BER_BVNULL;
2761
2762		to++;
2763		filter2bv( a->acl_filter, &bv );
2764		ptr = lutil_strcopy( ptr, " filter=\"" );
2765		ptr = lutil_strcopy( ptr, bv.bv_val );
2766		*ptr++ = '"';
2767		*ptr++ = '\n';
2768		ch_free( bv.bv_val );
2769	}
2770
2771	if ( a->acl_attrs != NULL ) {
2772		int	first = 1;
2773		AttributeName *an;
2774		to++;
2775
2776		ptr = lutil_strcopy( ptr, " attrs=" );
2777		for ( an = a->acl_attrs; an && !BER_BVISNULL( &an->an_name ); an++ ) {
2778			if ( ! first ) *ptr++ = ',';
2779			if (an->an_oc) {
2780				*ptr++ = ( an->an_flags & SLAP_AN_OCEXCLUDE ) ? '!' : '@';
2781				ptr = lutil_strcopy( ptr, an->an_oc->soc_cname.bv_val );
2782
2783			} else {
2784				ptr = lutil_strcopy( ptr, an->an_name.bv_val );
2785			}
2786			first = 0;
2787		}
2788		*ptr++ = '\n';
2789	}
2790
2791	if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
2792		to++;
2793		ptr = lutil_strcopy( ptr, " val." );
2794		if ( a->acl_attrval_style == ACL_STYLE_BASE &&
2795			a->acl_attrs[0].an_desc->ad_type->sat_syntax ==
2796				slap_schema.si_syn_distinguishedName )
2797			ptr = lutil_strcopy( ptr, style_base );
2798		else
2799			ptr = lutil_strcopy( ptr, style_strings[a->acl_attrval_style] );
2800		*ptr++ = '=';
2801		*ptr++ = '"';
2802		ptr = lutil_strcopy( ptr, a->acl_attrval.bv_val );
2803		*ptr++ = '"';
2804		*ptr++ = '\n';
2805	}
2806
2807	if( !to ) {
2808		ptr = lutil_strcopy( ptr, " *\n" );
2809	}
2810
2811	for ( b = a->acl_access; b != NULL; b = b->a_next ) {
2812		ptr = access2text( b, ptr );
2813	}
2814	*ptr = '\0';
2815	bv->bv_len = ptr - bv->bv_val;
2816}
2817
2818#ifdef LDAP_DEBUG
2819static void
2820print_acl( Backend *be, AccessControl *a )
2821{
2822	struct berval bv;
2823
2824	acl_unparse( a, &bv );
2825	fprintf( stderr, "%s ACL: access %s\n",
2826		be == NULL ? "Global" : "Backend", bv.bv_val );
2827}
2828#endif /* LDAP_DEBUG */
2829