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