Logo Search packages:      
Sourcecode: openldap version File versions

aclparse.c

/* aclparse.c - routines to parse and check acl's */
/* $OpenLDAP: pkg/ldap/servers/slapd/aclparse.c,v 1.198.2.6 2008/02/11 23:26:43 kurt Exp $ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 1998-2008 The OpenLDAP Foundation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* Portions Copyright (c) 1995 Regents of the University of Michigan.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of Michigan at Ann Arbor. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
 */

#include "portable.h"

#include <stdio.h>

#include <ac/ctype.h>
#include <ac/regex.h>
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/unistd.h>

#include "slap.h"
#include "lber_pvt.h"
#include "lutil.h"

static const char style_base[] = "base";
const char *style_strings[] = {
      "regex",
      "expand",
      "exact",
      "one",
      "subtree",
      "children",
      "level",
      "attrof",
      "anonymous",
      "users",
      "self",
      "ip",
      "ipv6",
      "path",
      NULL
};

static void       split(char *line, int splitchar, char **left, char **right);
static void       access_append(Access **l, Access *a);
static void       access_free( Access *a );
static int        acl_usage(void);

static void       acl_regex_normalized_dn(const char *src, struct berval *pat);

#ifdef LDAP_DEBUG
static void       print_acl(Backend *be, AccessControl *a);
#endif

static int        check_scope( BackendDB *be, AccessControl *a );

#ifdef SLAP_DYNACL
static int
slap_dynacl_config(
      const char *fname,
      int lineno,
      Access *b,
      const char *name,
      const char *opts,
      slap_style_t sty,
      const char *right )
{
      slap_dynacl_t     *da, *tmp;
      int         rc = 0;

      for ( da = b->a_dynacl; da; da = da->da_next ) {
            if ( strcasecmp( da->da_name, name ) == 0 ) {
                  Debug( LDAP_DEBUG_ANY,
                        "%s: line %d: dynacl \"%s\" already specified.\n",
                        fname, lineno, name );
                  return acl_usage();
            }
      }

      da = slap_dynacl_get( name );
      if ( da == NULL ) {
            return -1;
      }

      tmp = ch_malloc( sizeof( slap_dynacl_t ) );
      *tmp = *da;

      if ( tmp->da_parse ) {
            rc = ( *tmp->da_parse )( fname, lineno, opts, sty, right, &tmp->da_private );
            if ( rc ) {
                  ch_free( tmp );
                  return rc;
            }
      }

      tmp->da_next = b->a_dynacl;
      b->a_dynacl = tmp;

      return 0;
}
#endif /* SLAP_DYNACL */

static void
regtest(const char *fname, int lineno, char *pat) {
      int e;
      regex_t re;

      char        buf[ SLAP_TEXT_BUFLEN ];
      unsigned    size;

      char *sp;
      char *dp;
      int  flag;

      sp = pat;
      dp = buf;
      size = 0;
      buf[0] = '\0';

      for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
            if (flag) {
                  if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
                        *dp++ = *sp;
                        size++;
                  }
                  flag = 0;

            } else {
                  if (*sp == '$') {
                        flag = 1;
                  } else {
                        *dp++ = *sp;
                        size++;
                  }
            }
      }

      *dp = '\0';
      if ( size >= (sizeof(buf) - 1) ) {
            Debug( LDAP_DEBUG_ANY,
                  "%s: line %d: regular expression \"%s\" too large\n",
                  fname, lineno, pat );
            (void)acl_usage();
            exit( EXIT_FAILURE );
      }

      if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
            char error[ SLAP_TEXT_BUFLEN ];

            regerror(e, &re, error, sizeof(error));

            snprintf( buf, sizeof( buf ),
                  "regular expression \"%s\" bad because of %s",
                  pat, error );
            Debug( LDAP_DEBUG_ANY,
                  "%s: line %d: %s\n",
                  fname, lineno, buf );
            acl_usage();
            exit( EXIT_FAILURE );
      }
      regfree(&re);
}

/*
 * Experimental
 *
 * Check if the pattern of an ACL, if any, matches the scope
 * of the backend it is defined within.
 */
#define     ACL_SCOPE_UNKNOWN (-2)
#define     ACL_SCOPE_ERR           (-1)
#define     ACL_SCOPE_OK            (0)
#define     ACL_SCOPE_PARTIAL (1)
#define     ACL_SCOPE_WARN          (2)

static int
check_scope( BackendDB *be, AccessControl *a )
{
      ber_len_t   patlen;
      struct berval     dn;

      dn = be->be_nsuffix[0];

      if ( BER_BVISEMPTY( &dn ) ) {
            return ACL_SCOPE_OK;
      }

      if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
                  a->acl_dn_style != ACL_STYLE_REGEX )
      {
            slap_style_t      style = a->acl_dn_style;

            if ( style == ACL_STYLE_REGEX ) {
                  char        dnbuf[SLAP_LDAPDN_MAXLEN + 2];
                  char        rebuf[SLAP_LDAPDN_MAXLEN + 1];
                  ber_len_t   rebuflen;
                  regex_t           re;
                  int         rc;
                  
                  /* add trailing '$' to database suffix to form
                   * a simple trial regex pattern "<suffix>$" */
                  AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
                        be->be_nsuffix[0].bv_len );
                  dnbuf[be->be_nsuffix[0].bv_len] = '$';
                  dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';

                  if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
                        return ACL_SCOPE_WARN;
                  }

                  /* remove trailing ')$', if any, from original
                   * regex pattern */
                  rebuflen = a->acl_dn_pat.bv_len;
                  AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
                  if ( rebuf[rebuflen - 1] == '$' ) {
                        rebuf[--rebuflen] = '\0';
                  }
                  while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
                        rebuf[--rebuflen] = '\0';
                  }
                  if ( rebuflen == be->be_nsuffix[0].bv_len ) {
                        rc = ACL_SCOPE_WARN;
                        goto regex_done;
                  }

                  /* not a clear indication of scoping error, though */
                  rc = regexec( &re, rebuf, 0, NULL, 0 )
                        ? ACL_SCOPE_WARN : ACL_SCOPE_OK;

regex_done:;
                  regfree( &re );
                  return rc;
            }

            patlen = a->acl_dn_pat.bv_len;
            /* If backend suffix is longer than pattern,
             * it is a potential mismatch (in the sense
             * that a superior naming context could
             * match */
            if ( dn.bv_len > patlen ) {
                  /* base is blatantly wrong */
                  if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;

                  /* a style of one can be wrong if there is
                   * more than one level between the suffix
                   * and the pattern */
                  if ( style == ACL_STYLE_ONE ) {
                        ber_len_t   rdnlen = 0;
                        int         sep = 0;

                        if ( patlen > 0 ) {
                              if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
                                    return ACL_SCOPE_ERR;
                              }
                              sep = 1;
                        }

                        rdnlen = dn_rdnlen( NULL, &dn );
                        if ( rdnlen != dn.bv_len - patlen - sep )
                              return ACL_SCOPE_ERR;
                  }

                  /* if the trailing part doesn't match,
                   * then it's an error */
                  if ( strcmp( a->acl_dn_pat.bv_val,
                        &dn.bv_val[dn.bv_len - patlen] ) != 0 )
                  {
                        return ACL_SCOPE_ERR;
                  }

                  return ACL_SCOPE_PARTIAL;
            }

            switch ( style ) {
            case ACL_STYLE_BASE:
            case ACL_STYLE_ONE:
            case ACL_STYLE_CHILDREN:
            case ACL_STYLE_SUBTREE:
                  break;

            default:
                  assert( 0 );
                  break;
            }

            if ( dn.bv_len < patlen &&
                  !DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
            {
                  return ACL_SCOPE_ERR;
            }

            if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
                  != 0 )
            {
                  return ACL_SCOPE_ERR;
            }

            return ACL_SCOPE_OK;
      }

      return ACL_SCOPE_UNKNOWN;
}

int
parse_acl(
      Backend     *be,
      const char  *fname,
      int         lineno,
      int         argc,
      char        **argv,
      int         pos )
{
      int         i;
      char        *left, *right, *style;
      struct berval     bv;
      AccessControl     *a = NULL;
      Access      *b = NULL;
      int rc;
      const char *text;

      for ( i = 1; i < argc; i++ ) {
            /* to clause - select which entries are protected */
            if ( strcasecmp( argv[i], "to" ) == 0 ) {
                  if ( a != NULL ) {
                        Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                              "only one to clause allowed in access line\n",
                            fname, lineno, 0 );
                        goto fail;
                  }
                  a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
                  for ( ++i; i < argc; i++ ) {
                        if ( strcasecmp( argv[i], "by" ) == 0 ) {
                              i--;
                              break;
                        }

                        if ( strcasecmp( argv[i], "*" ) == 0 ) {
                              if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
                                    a->acl_dn_style != ACL_STYLE_REGEX )
                              {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: dn pattern"
                                          " already specified in to clause.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
                              continue;
                        }

                        split( argv[i], '=', &left, &right );
                        split( left, '.', &left, &style );

                        if ( right == NULL ) {
                              Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                    "missing \"=\" in \"%s\" in to clause\n",
                                  fname, lineno, left );
                              goto fail;
                        }

                        if ( strcasecmp( left, "dn" ) == 0 ) {
                              if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
                                    a->acl_dn_style != ACL_STYLE_REGEX )
                              {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: dn pattern"
                                          " already specified in to clause.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( style == NULL || *style == '\0' ||
                                    strcasecmp( style, "baseObject" ) == 0 ||
                                    strcasecmp( style, "base" ) == 0 ||
                                    strcasecmp( style, "exact" ) == 0 )
                              {
                                    a->acl_dn_style = ACL_STYLE_BASE;
                                    ber_str2bv( right, 0, 1, &a->acl_dn_pat );

                              } else if ( strcasecmp( style, "oneLevel" ) == 0 ||
                                    strcasecmp( style, "one" ) == 0 )
                              {
                                    a->acl_dn_style = ACL_STYLE_ONE;
                                    ber_str2bv( right, 0, 1, &a->acl_dn_pat );

                              } else if ( strcasecmp( style, "subtree" ) == 0 ||
                                    strcasecmp( style, "sub" ) == 0 )
                              {
                                    if( *right == '\0' ) {
                                          ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );

                                    } else {
                                          a->acl_dn_style = ACL_STYLE_SUBTREE;
                                          ber_str2bv( right, 0, 1, &a->acl_dn_pat );
                                    }

                              } else if ( strcasecmp( style, "children" ) == 0 ) {
                                    a->acl_dn_style = ACL_STYLE_CHILDREN;
                                    ber_str2bv( right, 0, 1, &a->acl_dn_pat );

                              } else if ( strcasecmp( style, "regex" ) == 0 ) {
                                    a->acl_dn_style = ACL_STYLE_REGEX;

                                    if ( *right == '\0' ) {
                                          /* empty regex should match empty DN */
                                          a->acl_dn_style = ACL_STYLE_BASE;
                                          ber_str2bv( right, 0, 1, &a->acl_dn_pat );

                                    } else if ( strcmp(right, "*") == 0 
                                          || strcmp(right, ".*") == 0 
                                          || strcmp(right, ".*$") == 0 
                                          || strcmp(right, "^.*") == 0 
                                          || strcmp(right, "^.*$") == 0
                                          || strcmp(right, ".*$$") == 0 
                                          || strcmp(right, "^.*$$") == 0 )
                                    {
                                          ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );

                                    } else {
                                          acl_regex_normalized_dn( right, &a->acl_dn_pat );
                                    }

                              } else {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "unknown dn style \"%s\" in to clause\n",
                                        fname, lineno, style );
                                    goto fail;
                              }

                              continue;
                        }

                        if ( strcasecmp( left, "filter" ) == 0 ) {
                              if ( (a->acl_filter = str2filter( right )) == NULL ) {
                                    Debug( LDAP_DEBUG_ANY,
                        "%s: line %d: bad filter \"%s\" in to clause\n",
                                        fname, lineno, right );
                                    goto fail;
                              }

                        } else if ( strcasecmp( left, "attr" ) == 0           /* TOLERATED */
                                    || strcasecmp( left, "attrs" ) == 0 )     /* DOCUMENTED */
                        {
                              if ( strcasecmp( left, "attr" ) == 0 ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: \"attr\" "
                                          "is deprecated (and undocumented); "
                                          "use \"attrs\" instead.\n",
                                          fname, lineno, 0 );
                              }

                              a->acl_attrs = str2anlist( a->acl_attrs,
                                    right, "," );
                              if ( a->acl_attrs == NULL ) {
                                    Debug( LDAP_DEBUG_ANY,
                        "%s: line %d: unknown attr \"%s\" in to clause\n",
                                        fname, lineno, right );
                                    goto fail;
                              }

                        } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
                              struct berval     bv;
                              char        *mr;
                              
                              if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
                                    Debug( LDAP_DEBUG_ANY,
                        "%s: line %d: attr val already specified in to clause.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }
                              if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
                              {
                                    Debug( LDAP_DEBUG_ANY,
                        "%s: line %d: attr val requires a single attribute.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              ber_str2bv( right, 0, 0, &bv );
                              a->acl_attrval_style = ACL_STYLE_BASE;

                              mr = strchr( left, '/' );
                              if ( mr != NULL ) {
                                    mr[ 0 ] = '\0';
                                    mr++;

                                    a->acl_attrval_mr = mr_find( mr );
                                    if ( a->acl_attrval_mr == NULL ) {
                                          Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                "invalid matching rule \"%s\".\n",
                                                fname, lineno, mr );
                                          goto fail;
                                    }

                                    if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) )
                                    {
                                          char  buf[ SLAP_TEXT_BUFLEN ];

                                          snprintf( buf, sizeof( buf ),
                                                "matching rule \"%s\" use "
                                                "with attr \"%s\" not appropriate.",
                                                mr, a->acl_attrs[ 0 ].an_name.bv_val );
                                                

                                          Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
                                                fname, lineno, buf );
                                          goto fail;
                                    }
                              }
                              
                              if ( style != NULL ) {
                                    if ( strcasecmp( style, "regex" ) == 0 ) {
                                          int e = regcomp( &a->acl_attrval_re, bv.bv_val,
                                                REG_EXTENDED | REG_ICASE | REG_NOSUB );
                                          if ( e ) {
                                                char  err[SLAP_TEXT_BUFLEN],
                                                      buf[ SLAP_TEXT_BUFLEN ];

                                                regerror( e, &a->acl_attrval_re, err, sizeof( err ) );

                                                snprintf( buf, sizeof( buf ),
                                                      "regular expression \"%s\" bad because of %s",
                                                      right, err );

                                                Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
                                                      fname, lineno, buf );
                                                goto fail;
                                          }
                                          a->acl_attrval_style = ACL_STYLE_REGEX;

                                    } else {
                                          /* FIXME: if the attribute has DN syntax, we might
                                           * allow one, subtree and children styles as well */
                                          if ( !strcasecmp( style, "base" ) ||
                                                !strcasecmp( style, "exact" ) ) {
                                                a->acl_attrval_style = ACL_STYLE_BASE;

                                          } else if ( a->acl_attrs[0].an_desc->ad_type->
                                                sat_syntax == slap_schema.si_syn_distinguishedName )
                                          {
                                                if ( !strcasecmp( style, "baseObject" ) ||
                                                      !strcasecmp( style, "base" ) )
                                                {
                                                      a->acl_attrval_style = ACL_STYLE_BASE;
                                                } else if ( !strcasecmp( style, "onelevel" ) ||
                                                      !strcasecmp( style, "one" ) )
                                                {
                                                      a->acl_attrval_style = ACL_STYLE_ONE;
                                                } else if ( !strcasecmp( style, "subtree" ) ||
                                                      !strcasecmp( style, "sub" ) )
                                                {
                                                      a->acl_attrval_style = ACL_STYLE_SUBTREE;
                                                } else if ( !strcasecmp( style, "children" ) ) {
                                                      a->acl_attrval_style = ACL_STYLE_CHILDREN;
                                                } else {
                                                      char  buf[ SLAP_TEXT_BUFLEN ];

                                                      snprintf( buf, sizeof( buf ),
                                                            "unknown val.<style> \"%s\" for attributeType \"%s\" "
                                                                  "with DN syntax.",
                                                            style,
                                                            a->acl_attrs[0].an_desc->ad_cname.bv_val );

                                                      Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, 
                                                            "%s: line %d: %s\n",
                                                            fname, lineno, buf );
                                                      goto fail;
                                                }

                                                rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL );
                                                if ( rc != LDAP_SUCCESS ) {
                                                      char  buf[ SLAP_TEXT_BUFLEN ];

                                                      snprintf( buf, sizeof( buf ),
                                                            "unable to normalize DN \"%s\" "
                                                            "for attributeType \"%s\" (%d).",
                                                            bv.bv_val,
                                                            a->acl_attrs[0].an_desc->ad_cname.bv_val,
                                                            rc );
                                                      Debug( LDAP_DEBUG_ANY, 
                                                            "%s: line %d: %s\n",
                                                            fname, lineno, buf );
                                                      goto fail;
                                                }

                                          } else {
                                                char  buf[ SLAP_TEXT_BUFLEN ];

                                                snprintf( buf, sizeof( buf ),
                                                      "unknown val.<style> \"%s\" for attributeType \"%s\".",
                                                      style, a->acl_attrs[0].an_desc->ad_cname.bv_val );
                                                Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, 
                                                      "%s: line %d: %s\n",
                                                      fname, lineno, buf );
                                                goto fail;
                                          }
                                    }
                              }

                              /* Check for appropriate matching rule */
                              if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
                                    ber_dupbv( &a->acl_attrval, &bv );

                              } else if ( BER_BVISNULL( &a->acl_attrval ) ) {
                                    int         rc;
                                    const char  *text;

                                    if ( a->acl_attrval_mr == NULL ) {
                                          a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality;
                                    }

                                    if ( a->acl_attrval_mr == NULL ) {
                                          Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                "attr \"%s\" does not have an EQUALITY matching rule.\n",
                                                fname, lineno, a->acl_attrs[ 0 ].an_name.bv_val );
                                          goto fail;
                                    }

                                    rc = asserted_value_validate_normalize(
                                          a->acl_attrs[ 0 ].an_desc,
                                          a->acl_attrval_mr,
                                          SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
                                          &bv,
                                          &a->acl_attrval,
                                          &text,
                                          NULL );
                                    if ( rc != LDAP_SUCCESS ) {
                                          char  buf[ SLAP_TEXT_BUFLEN ];

                                          snprintf( buf, sizeof( buf ), "%s: line %d: "
                                                " attr \"%s\" normalization failed (%d: %s)",
                                                fname, lineno,
                                                a->acl_attrs[ 0 ].an_name.bv_val, rc, text );
                                          Debug( LDAP_DEBUG_ANY, "%s: line %d: %s.\n",
                                                fname, lineno, buf );
                                          goto fail;
                                    }
                              }

                        } else {
                              Debug( LDAP_DEBUG_ANY,
                                    "%s: line %d: expecting <what> got \"%s\"\n",
                                  fname, lineno, left );
                              goto fail;
                        }
                  }

                  if ( !BER_BVISNULL( &a->acl_dn_pat ) && 
                              ber_bvccmp( &a->acl_dn_pat, '*' ) )
                  {
                        free( a->acl_dn_pat.bv_val );
                        BER_BVZERO( &a->acl_dn_pat );
                        a->acl_dn_style = ACL_STYLE_REGEX;
                  }
                  
                  if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
                              a->acl_dn_style != ACL_STYLE_REGEX ) 
                  {
                        if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
                              struct berval bv;
                              rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
                              if ( rc != LDAP_SUCCESS ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: bad DN \"%s\" in to DN clause\n",
                                          fname, lineno, a->acl_dn_pat.bv_val );
                                    goto fail;
                              }
                              free( a->acl_dn_pat.bv_val );
                              a->acl_dn_pat = bv;

                        } else {
                              int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
                                    REG_EXTENDED | REG_ICASE );
                              if ( e ) {
                                    char  err[ SLAP_TEXT_BUFLEN ],
                                          buf[ SLAP_TEXT_BUFLEN ];

                                    regerror( e, &a->acl_dn_re, err, sizeof( err ) );
                                    snprintf( buf, sizeof( buf ),
                                          "regular expression \"%s\" bad because of %s",
                                          right, err );
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
                                          fname, lineno, buf );
                                    goto fail;
                              }
                        }
                  }

            /* by clause - select who has what access to entries */
            } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
                  if ( a == NULL ) {
                        Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                              "to clause required before by clause in access line\n",
                              fname, lineno, 0 );
                        goto fail;
                  }

                  /*
                   * by clause consists of <who> and <access>
                   */

                  if ( ++i == argc ) {
                        Debug( LDAP_DEBUG_ANY,
                              "%s: line %d: premature EOL: expecting <who>\n",
                              fname, lineno, 0 );
                        goto fail;
                  }

                  b = (Access *) ch_calloc( 1, sizeof(Access) );

                  ACL_INVALIDATE( b->a_access_mask );

                  /* get <who> */
                  for ( ; i < argc; i++ ) {
                        slap_style_t      sty = ACL_STYLE_REGEX;
                        char        *style_modifier = NULL;
                        char        *style_level = NULL;
                        int         level = 0;
                        int         expand = 0;
                        slap_dn_access    *bdn = &b->a_dn;
                        int         is_realdn = 0;

                        split( argv[i], '=', &left, &right );
                        split( left, '.', &left, &style );
                        if ( style ) {
                              split( style, ',', &style, &style_modifier );

                              if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
                                    split( style, '{', &style, &style_level );
                                    if ( style_level != NULL ) {
                                          char *p = strchr( style_level, '}' );
                                          if ( p == NULL ) {
                                                Debug( LDAP_DEBUG_ANY,
                                                      "%s: line %d: premature eol: "
                                                      "expecting closing '}' in \"level{n}\"\n",
                                                      fname, lineno, 0 );
                                                goto fail;
                                          } else if ( p == style_level ) {
                                                Debug( LDAP_DEBUG_ANY,
                                                      "%s: line %d: empty level "
                                                      "in \"level{n}\"\n",
                                                      fname, lineno, 0 );
                                                goto fail;
                                          }
                                          p[0] = '\0';
                                    }
                              }
                        }

                        if ( style == NULL || *style == '\0' ||
                              strcasecmp( style, "exact" ) == 0 ||
                              strcasecmp( style, "baseObject" ) == 0 ||
                              strcasecmp( style, "base" ) == 0 )
                        {
                              sty = ACL_STYLE_BASE;

                        } else if ( strcasecmp( style, "onelevel" ) == 0 ||
                              strcasecmp( style, "one" ) == 0 )
                        {
                              sty = ACL_STYLE_ONE;

                        } else if ( strcasecmp( style, "subtree" ) == 0 ||
                              strcasecmp( style, "sub" ) == 0 )
                        {
                              sty = ACL_STYLE_SUBTREE;

                        } else if ( strcasecmp( style, "children" ) == 0 ) {
                              sty = ACL_STYLE_CHILDREN;

                        } else if ( strcasecmp( style, "level" ) == 0 )
                        {
                              if ( lutil_atoi( &level, style_level ) != 0 ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: unable to parse level "
                                          "in \"level{n}\"\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              sty = ACL_STYLE_LEVEL;

                        } else if ( strcasecmp( style, "regex" ) == 0 ) {
                              sty = ACL_STYLE_REGEX;

                        } else if ( strcasecmp( style, "expand" ) == 0 ) {
                              sty = ACL_STYLE_EXPAND;

                        } else if ( strcasecmp( style, "ip" ) == 0 ) {
                              sty = ACL_STYLE_IP;

                        } else if ( strcasecmp( style, "ipv6" ) == 0 ) {
#ifndef LDAP_PF_INET6
                              Debug( LDAP_DEBUG_ANY,
                                    "%s: line %d: IPv6 not supported\n",
                                    fname, lineno, 0 );
#endif /* ! LDAP_PF_INET6 */
                              sty = ACL_STYLE_IPV6;

                        } else if ( strcasecmp( style, "path" ) == 0 ) {
                              sty = ACL_STYLE_PATH;
#ifndef LDAP_PF_LOCAL
                              Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
                                    "%s: line %d: "
                                    "\"path\" style modifier is useless without local.\n",
                                    fname, lineno, 0 );
                              goto fail;
#endif /* LDAP_PF_LOCAL */

                        } else {
                              Debug( LDAP_DEBUG_ANY,
                                    "%s: line %d: unknown style \"%s\" in by clause\n",
                                    fname, lineno, style );
                              goto fail;
                        }

                        if ( style_modifier &&
                              strcasecmp( style_modifier, "expand" ) == 0 )
                        {
                              switch ( sty ) {
                              case ACL_STYLE_REGEX:
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "\"regex\" style implies \"expand\" modifier.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                                    break;

                              case ACL_STYLE_EXPAND:
                                    break;

                              default:
                                    /* we'll see later if it's pertinent */
                                    expand = 1;
                                    break;
                              }
                        }

                        /* expand in <who> needs regex in <what> */
                        if ( ( sty == ACL_STYLE_EXPAND || expand )
                                    && a->acl_dn_style != ACL_STYLE_REGEX )
                        {
                              Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, "%s: line %d: \"expand\" style "
                                    "or modifier used in conjunction with a non-regex <what> clause.\n",
                                    fname, lineno, 0 );
                                    goto fail;
                        }

                        if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
                              is_realdn = 1;
                              bdn = &b->a_realdn;
                              left += STRLENOF( "real" );
                        }

                        if ( strcasecmp( left, "*" ) == 0 ) {
                              if ( is_realdn ) {
                                    goto fail;
                              }

                              ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
                              sty = ACL_STYLE_REGEX;

                        } else if ( strcasecmp( left, "anonymous" ) == 0 ) {
                              ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
                              sty = ACL_STYLE_ANONYMOUS;

                        } else if ( strcasecmp( left, "users" ) == 0 ) {
                              ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
                              sty = ACL_STYLE_USERS;

                        } else if ( strcasecmp( left, "self" ) == 0 ) {
                              ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
                              sty = ACL_STYLE_SELF;

                        } else if ( strcasecmp( left, "dn" ) == 0 ) {
                              if ( sty == ACL_STYLE_REGEX ) {
                                    bdn->a_style = ACL_STYLE_REGEX;
                                    if ( right == NULL ) {
                                          /* no '=' */
                                          ber_str2bv("users",
                                                STRLENOF( "users" ),
                                                1, &bv);
                                          bdn->a_style = ACL_STYLE_USERS;

                                    } else if (*right == '\0' ) {
                                          /* dn="" */
                                          ber_str2bv("anonymous",
                                                STRLENOF( "anonymous" ),
                                                1, &bv);
                                          bdn->a_style = ACL_STYLE_ANONYMOUS;

                                    } else if ( strcmp( right, "*" ) == 0 ) {
                                          /* dn=* */
                                          /* any or users?  users for now */
                                          ber_str2bv("users",
                                                STRLENOF( "users" ),
                                                1, &bv);
                                          bdn->a_style = ACL_STYLE_USERS;

                                    } else if ( strcmp( right, ".+" ) == 0
                                          || strcmp( right, "^.+" ) == 0
                                          || strcmp( right, ".+$" ) == 0
                                          || strcmp( right, "^.+$" ) == 0
                                          || strcmp( right, ".+$$" ) == 0
                                          || strcmp( right, "^.+$$" ) == 0 )
                                    {
                                          ber_str2bv("users",
                                                STRLENOF( "users" ),
                                                1, &bv);
                                          bdn->a_style = ACL_STYLE_USERS;

                                    } else if ( strcmp( right, ".*" ) == 0
                                          || strcmp( right, "^.*" ) == 0
                                          || strcmp( right, ".*$" ) == 0
                                          || strcmp( right, "^.*$" ) == 0
                                          || strcmp( right, ".*$$" ) == 0
                                          || strcmp( right, "^.*$$" ) == 0 )
                                    {
                                          ber_str2bv("*",
                                                STRLENOF( "*" ),
                                                1, &bv);

                                    } else {
                                          acl_regex_normalized_dn( right, &bv );
                                          if ( !ber_bvccmp( &bv, '*' ) ) {
                                                regtest( fname, lineno, bv.bv_val );
                                          }
                                    }

                              } else if ( right == NULL || *right == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "missing \"=\" in (or value after) \"%s\" "
                                          "in by clause\n",
                                          fname, lineno, left );
                                    goto fail;

                              } else {
                                    ber_str2bv( right, 0, 1, &bv );
                              }

                        } else {
                              BER_BVZERO( &bv );
                        }

                        if ( !BER_BVISNULL( &bv ) ) {
                              if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: dn pattern already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( sty != ACL_STYLE_REGEX &&
                                          sty != ACL_STYLE_ANONYMOUS &&
                                          sty != ACL_STYLE_USERS &&
                                          sty != ACL_STYLE_SELF &&
                                          expand == 0 )
                              {
                                    rc = dnNormalize(0, NULL, NULL,
                                          &bv, &bdn->a_pat, NULL);
                                    if ( rc != LDAP_SUCCESS ) {
                                          Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: bad DN \"%s\" in by DN clause\n",
                                                fname, lineno, bv.bv_val );
                                          goto fail;
                                    }
                                    free( bv.bv_val );
                                    if ( sty == ACL_STYLE_BASE
                                          && be != NULL
                                          && !BER_BVISNULL( &be->be_rootndn )
                                          && dn_match( &bdn->a_pat, &be->be_rootndn ) )
                                    {
                                          Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: rootdn is always granted "
                                                "unlimited privileges.\n",
                                                fname, lineno, 0 );
                                    }

                              } else {
                                    bdn->a_pat = bv;
                              }
                              bdn->a_style = sty;
                              if ( expand ) {
                                    char  *exp;
                                    int   gotit = 0;

                                    for ( exp = strchr( bdn->a_pat.bv_val, '$' );
                                          exp && (ber_len_t)(exp - bdn->a_pat.bv_val)
                                                < bdn->a_pat.bv_len;
                                          exp = strchr( exp, '$' ) )
                                    {
                                          if ( isdigit( (unsigned char) exp[ 1 ] ) ) {
                                                gotit = 1;
                                                break;
                                          }
                                    }

                                    if ( gotit == 1 ) {
                                          bdn->a_expand = expand;

                                    } else {
                                          Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                "\"expand\" used with no expansions in \"pattern\".\n",
                                                fname, lineno, 0 );
                                          goto fail;
                                    } 
                              }
                              if ( sty == ACL_STYLE_SELF ) {
                                    bdn->a_self_level = level;

                              } else {
                                    if ( level < 0 ) {
                                          Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: bad negative level \"%d\" "
                                                "in by DN clause\n",
                                                fname, lineno, level );
                                          goto fail;
                                    } else if ( level == 1 ) {
                                          Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: \"onelevel\" should be used "
                                                "instead of \"level{1}\" in by DN clause\n",
                                                fname, lineno, 0 );
                                    } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
                                          Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: \"base\" should be used "
                                                "instead of \"level{0}\" in by DN clause\n",
                                                fname, lineno, 0 );
                                    }

                                    bdn->a_level = level;
                              }
                              continue;
                        }

                        if ( strcasecmp( left, "dnattr" ) == 0 ) {
                              if ( right == NULL || right[0] == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "missing \"=\" in (or value after) \"%s\" "
                                          "in by clause\n",
                                          fname, lineno, left );
                                    goto fail;
                              }

                              if( bdn->a_at != NULL ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: dnattr already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              rc = slap_str2ad( right, &bdn->a_at, &text );

                              if( rc != LDAP_SUCCESS ) {
                                    char  buf[ SLAP_TEXT_BUFLEN ];

                                    snprintf( buf, sizeof( buf ),
                                          "dnattr \"%s\": %s",
                                          right, text );
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: %s\n",
                                          fname, lineno, buf );
                                    goto fail;
                              }


                              if( !is_at_syntax( bdn->a_at->ad_type,
                                    SLAPD_DN_SYNTAX ) &&
                                    !is_at_syntax( bdn->a_at->ad_type,
                                    SLAPD_NAMEUID_SYNTAX ))
                              {
                                    char  buf[ SLAP_TEXT_BUFLEN ];

                                    snprintf( buf, sizeof( buf ),
                                          "dnattr \"%s\": "
                                          "inappropriate syntax: %s\n",
                                          right,
                                          bdn->a_at->ad_type->sat_syntax_oid );
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: %s\n",
                                          fname, lineno, buf );
                                    goto fail;
                              }

                              if( bdn->a_at->ad_type->sat_equality == NULL ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: dnattr \"%s\": "
                                          "inappropriate matching (no EQUALITY)\n",
                                          fname, lineno, right );
                                    goto fail;
                              }

                              continue;
                        }

                        if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
                              char *name = NULL;
                              char *value = NULL;
                              char *attr_name = SLAPD_GROUP_ATTR;

                              switch ( sty ) {
                              case ACL_STYLE_REGEX:
                                    /* legacy, tolerated */
                                    Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
                                          "%s: line %d: "
                                          "deprecated group style \"regex\"; "
                                          "use \"expand\" instead.\n",
                                          fname, lineno, 0 );
                                    sty = ACL_STYLE_EXPAND;
                                    break;

                              case ACL_STYLE_BASE:
                                    /* legal, traditional */
                              case ACL_STYLE_EXPAND:
                                    /* legal, substring expansion; supersedes regex */
                                    break;

                              default:
                                    /* unknown */
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause.\n",
                                          fname, lineno, style );
                                    goto fail;
                              }

                              if ( right == NULL || right[0] == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: "
                                          "missing \"=\" in (or value after) \"%s\" "
                                          "in by clause.\n",
                                          fname, lineno, left );
                                    goto fail;
                              }

                              if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: group pattern already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              /* format of string is
                                    "group/objectClassValue/groupAttrName" */
                              if ( ( value = strchr(left, '/') ) != NULL ) {
                                    *value++ = '\0';
                                    if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
                                          *name++ = '\0';
                                    }
                              }

                              b->a_group_style = sty;
                              if ( sty == ACL_STYLE_EXPAND ) {
                                    acl_regex_normalized_dn( right, &bv );
                                    if ( !ber_bvccmp( &bv, '*' ) ) {
                                          regtest( fname, lineno, bv.bv_val );
                                    }
                                    b->a_group_pat = bv;

                              } else {
                                    ber_str2bv( right, 0, 0, &bv );
                                    rc = dnNormalize( 0, NULL, NULL, &bv,
                                          &b->a_group_pat, NULL );
                                    if ( rc != LDAP_SUCCESS ) {
                                          Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: bad DN \"%s\".\n",
                                                fname, lineno, right );
                                          goto fail;
                                    }
                              }

                              if ( value && *value ) {
                                    b->a_group_oc = oc_find( value );
                                    *--value = '/';

                                    if ( b->a_group_oc == NULL ) {
                                          Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: group objectclass "
                                                "\"%s\" unknown.\n",
                                                fname, lineno, value );
                                          goto fail;
                                    }

                              } else {
                                    b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );

                                    if( b->a_group_oc == NULL ) {
                                          Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: group default objectclass "
                                                "\"%s\" unknown.\n",
                                                fname, lineno, SLAPD_GROUP_CLASS );
                                          goto fail;
                                    }
                              }

                              if ( is_object_subclass( slap_schema.si_oc_referral,
                                    b->a_group_oc ) )
                              {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: group objectclass \"%s\" "
                                          "is subclass of referral.\n",
                                          fname, lineno, value );
                                    goto fail;
                              }

                              if ( is_object_subclass( slap_schema.si_oc_alias,
                                    b->a_group_oc ) )
                              {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: group objectclass \"%s\" "
                                          "is subclass of alias.\n",
                                          fname, lineno, value );
                                    goto fail;
                              }

                              if ( name && *name ) {
                                    attr_name = name;
                                    *--name = '/';

                              }

                              rc = slap_str2ad( attr_name, &b->a_group_at, &text );
                              if ( rc != LDAP_SUCCESS ) {
                                    char  buf[ SLAP_TEXT_BUFLEN ];

                                    snprintf( buf, sizeof( buf ),
                                          "group \"%s\": %s.",
                                          right, text );
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: %s\n",
                                          fname, lineno, buf );
                                    goto fail;
                              }

                              if ( !is_at_syntax( b->a_group_at->ad_type,
                                          SLAPD_DN_SYNTAX ) /* e.g. "member" */
                                    && !is_at_syntax( b->a_group_at->ad_type,
                                          SLAPD_NAMEUID_SYNTAX ) /* e.g. memberUID */
                                    && !is_at_subtype( b->a_group_at->ad_type,
                                          slap_schema.si_ad_labeledURI->ad_type ) /* e.g. memberURL */ )
                              {
                                    char  buf[ SLAP_TEXT_BUFLEN ];

                                    snprintf( buf, sizeof( buf ),
                                          "group \"%s\" attr \"%s\": inappropriate syntax: %s; "
                                          "must be " SLAPD_DN_SYNTAX " (DN), "
                                          SLAPD_NAMEUID_SYNTAX " (NameUID) "
                                          "or a subtype of labeledURI.",
                                          right,
                                          attr_name,
                                          at_syntax( b->a_group_at->ad_type ) );
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: %s\n",
                                          fname, lineno, buf );
                                    goto fail;
                              }


                              {
                                    int rc;
                                    ObjectClass *ocs[2];

                                    ocs[0] = b->a_group_oc;
                                    ocs[1] = NULL;

                                    rc = oc_check_allowed( b->a_group_at->ad_type,
                                          ocs, NULL );

                                    if( rc != 0 ) {
                                          char  buf[ SLAP_TEXT_BUFLEN ];

                                          snprintf( buf, sizeof( buf ),
                                                "group: \"%s\" not allowed by \"%s\".",
                                                b->a_group_at->ad_cname.bv_val,
                                                b->a_group_oc->soc_oid );
                                          Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
                                                fname, lineno, buf );
                                          goto fail;
                                    }
                              }
                              continue;
                        }

                        if ( strcasecmp( left, "peername" ) == 0 ) {
                              switch ( sty ) {
                              case ACL_STYLE_REGEX:
                              case ACL_STYLE_BASE:
                                    /* legal, traditional */
                              case ACL_STYLE_EXPAND:
                                    /* cheap replacement to regex for simple expansion */
                              case ACL_STYLE_IP:
                              case ACL_STYLE_IPV6:
                              case ACL_STYLE_PATH:
                                    /* legal, peername specific */
                                    break;

                              default:
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause.\n",
                                        fname, lineno, style );
                                    goto fail;
                              }

                              if ( right == NULL || right[0] == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "missing \"=\" in (or value after) \"%s\" "
                                          "in by clause.\n",
                                          fname, lineno, left );
                                    goto fail;
                              }

                              if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "peername pattern already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              b->a_peername_style = sty;
                              if ( sty == ACL_STYLE_REGEX ) {
                                    acl_regex_normalized_dn( right, &bv );
                                    if ( !ber_bvccmp( &bv, '*' ) ) {
                                          regtest( fname, lineno, bv.bv_val );
                                    }
                                    b->a_peername_pat = bv;

                              } else {
                                    ber_str2bv( right, 0, 1, &b->a_peername_pat );

                                    if ( sty == ACL_STYLE_IP ) {
                                          char        *addr = NULL,
                                                      *mask = NULL,
                                                      *port = NULL;

                                          split( right, '{', &addr, &port );
                                          split( addr, '%', &addr, &mask );

                                          b->a_peername_addr = inet_addr( addr );
                                          if ( b->a_peername_addr == (unsigned long)(-1) ) {
                                                /* illegal address */
                                                Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                      "illegal peername address \"%s\".\n",
                                                      fname, lineno, addr );
                                                goto fail;
                                          }

                                          b->a_peername_mask = (unsigned long)(-1);
                                          if ( mask != NULL ) {
                                                b->a_peername_mask = inet_addr( mask );
                                                if ( b->a_peername_mask ==
                                                      (unsigned long)(-1) )
                                                {
                                                      /* illegal mask */
                                                      Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                            "illegal peername address mask "
                                                            "\"%s\".\n",
                                                            fname, lineno, mask );
                                                      goto fail;
                                                }
                                          } 

                                          b->a_peername_port = -1;
                                          if ( port ) {
                                                char  *end = NULL;

                                                b->a_peername_port = strtol( port, &end, 10 );
                                                if ( end == port || end[0] != '}' ) {
                                                      /* illegal port */
                                                      Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                            "illegal peername port specification "
                                                            "\"{%s}\".\n",
                                                            fname, lineno, port );
                                                      goto fail;
                                                }
                                          }

#ifdef LDAP_PF_INET6
                                    } else if ( sty == ACL_STYLE_IPV6 ) {
                                          char        *addr = NULL,
                                                      *mask = NULL,
                                                      *port = NULL;

                                          split( right, '{', &addr, &port );
                                          split( addr, '%', &addr, &mask );

                                          if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) {
                                                /* illegal address */
                                                Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                      "illegal peername address \"%s\".\n",
                                                      fname, lineno, addr );
                                                goto fail;
                                          }

                                          if ( mask == NULL ) {
                                                mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
                                          }

                                          if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) {
                                                /* illegal mask */
                                                Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                      "illegal peername address mask "
                                                      "\"%s\".\n",
                                                      fname, lineno, mask );
                                                goto fail;
                                          }

                                          b->a_peername_port = -1;
                                          if ( port ) {
                                                char  *end = NULL;

                                                b->a_peername_port = strtol( port, &end, 10 );
                                                if ( end == port || end[0] != '}' ) {
                                                      /* illegal port */
                                                      Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                            "illegal peername port specification "
                                                            "\"{%s}\".\n",
                                                            fname, lineno, port );
                                                      goto fail;
                                                }
                                          }
#endif /* LDAP_PF_INET6 */
                                    }
                              }
                              continue;
                        }

                        if ( strcasecmp( left, "sockname" ) == 0 ) {
                              switch ( sty ) {
                              case ACL_STYLE_REGEX:
                              case ACL_STYLE_BASE:
                                    /* legal, traditional */
                              case ACL_STYLE_EXPAND:
                                    /* cheap replacement to regex for simple expansion */
                                    break;

                              default:
                                    /* unknown */
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause\n",
                                        fname, lineno, style );
                                    goto fail;
                              }

                              if ( right == NULL || right[0] == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "missing \"=\" in (or value after) \"%s\" "
                                          "in by clause\n",
                                          fname, lineno, left );
                                    goto fail;
                              }

                              if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "sockname pattern already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              b->a_sockname_style = sty;
                              if ( sty == ACL_STYLE_REGEX ) {
                                    acl_regex_normalized_dn( right, &bv );
                                    if ( !ber_bvccmp( &bv, '*' ) ) {
                                          regtest( fname, lineno, bv.bv_val );
                                    }
                                    b->a_sockname_pat = bv;
                                    
                              } else {
                                    ber_str2bv( right, 0, 1, &b->a_sockname_pat );
                              }
                              continue;
                        }

                        if ( strcasecmp( left, "domain" ) == 0 ) {
                              switch ( sty ) {
                              case ACL_STYLE_REGEX:
                              case ACL_STYLE_BASE:
                              case ACL_STYLE_SUBTREE:
                                    /* legal, traditional */
                                    break;

                              case ACL_STYLE_EXPAND:
                                    /* tolerated: means exact,expand */
                                    if ( expand ) {
                                          Debug( LDAP_DEBUG_ANY,
                                                "%s: line %d: "
                                                "\"expand\" modifier "
                                                "with \"expand\" style.\n",
                                                fname, lineno, 0 );
                                    }
                                    sty = ACL_STYLE_BASE;
                                    expand = 1;
                                    break;

                              default:
                                    /* unknown */
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause.\n",
                                        fname, lineno, style );
                                    goto fail;
                              }

                              if ( right == NULL || right[0] == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "missing \"=\" in (or value after) \"%s\" "
                                          "in by clause.\n",
                                          fname, lineno, left );
                                    goto fail;
                              }

                              if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: domain pattern already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              b->a_domain_style = sty;
                              b->a_domain_expand = expand;
                              if ( sty == ACL_STYLE_REGEX ) {
                                    acl_regex_normalized_dn( right, &bv );
                                    if ( !ber_bvccmp( &bv, '*' ) ) {
                                          regtest( fname, lineno, bv.bv_val );
                                    }
                                    b->a_domain_pat = bv;

                              } else {
                                    ber_str2bv( right, 0, 1, &b->a_domain_pat );
                              }
                              continue;
                        }

                        if ( strcasecmp( left, "sockurl" ) == 0 ) {
                              switch ( sty ) {
                              case ACL_STYLE_REGEX:
                              case ACL_STYLE_BASE:
                                    /* legal, traditional */
                              case ACL_STYLE_EXPAND:
                                    /* cheap replacement to regex for simple expansion */
                                    break;

                              default:
                                    /* unknown */
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause.\n",
                                        fname, lineno, style );
                                    goto fail;
                              }

                              if ( right == NULL || right[0] == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "missing \"=\" in (or value after) \"%s\" "
                                          "in by clause.\n",
                                          fname, lineno, left );
                                    goto fail;
                              }

                              if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: sockurl pattern already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              b->a_sockurl_style = sty;
                              if ( sty == ACL_STYLE_REGEX ) {
                                    acl_regex_normalized_dn( right, &bv );
                                    if ( !ber_bvccmp( &bv, '*' ) ) {
                                          regtest( fname, lineno, bv.bv_val );
                                    }
                                    b->a_sockurl_pat = bv;
                                    
                              } else {
                                    ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
                              }
                              continue;
                        }

                        if ( strcasecmp( left, "set" ) == 0 ) {
                              switch ( sty ) {
                                    /* deprecated */
                              case ACL_STYLE_REGEX:
                                    Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
                                          "%s: line %d: "
                                          "deprecated set style "
                                          "\"regex\" in <by> clause; "
                                          "use \"expand\" instead.\n",
                                          fname, lineno, 0 );
                                    sty = ACL_STYLE_EXPAND;
                                    /* FALLTHRU */
                                    
                              case ACL_STYLE_BASE:
                              case ACL_STYLE_EXPAND:
                                    break;

                              default:
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause.\n",
                                          fname, lineno, style );
                                    goto fail;
                              }

                              if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: set attribute already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( right == NULL || *right == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: no set is defined.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              b->a_set_style = sty;
                              ber_str2bv( right, 0, 1, &b->a_set_pat );

                              continue;
                        }

#ifdef SLAP_DYNACL
                        {
                              char        *name = NULL,
                                          *opts = NULL;

#if 1 /* tolerate legacy "aci" <who> */
                              if ( strcasecmp( left, "aci" ) == 0 ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "undocumented deprecated \"aci\" directive "
                                          "is superseded by \"dynacl/aci\".\n",
                                          fname, lineno, 0 );
                                    name = "aci";
                                    
                              } else
#endif /* tolerate legacy "aci" <who> */
                              if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
                                    name = &left[ STRLENOF( "dynacl/" ) ];
                                    opts = strchr( name, '/' );
                                    if ( opts ) {
                                          opts[ 0 ] = '\0';
                                          opts++;
                                    }
                              }

                              if ( name ) {
                                    if ( slap_dynacl_config( fname, lineno, b, name, opts, sty, right ) ) {
                                          Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                                "unable to configure dynacl \"%s\".\n",
                                                fname, lineno, name );
                                          goto fail;
                                    }

                                    continue;
                              }
                        }
#endif /* SLAP_DYNACL */

                        if ( strcasecmp( left, "ssf" ) == 0 ) {
                              if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause.\n",
                                        fname, lineno, style );
                                    goto fail;
                              }

                              if ( b->a_authz.sai_ssf ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: ssf attribute already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( right == NULL || *right == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: no ssf is defined.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( lutil_atou( &b->a_authz.sai_ssf, right ) != 0 ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: unable to parse ssf value (%s).\n",
                                          fname, lineno, right );
                                    goto fail;
                              }

                              if ( !b->a_authz.sai_ssf ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: invalid ssf value (%s).\n",
                                          fname, lineno, right );
                                    goto fail;
                              }
                              continue;
                        }

                        if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
                              if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause.\n",
                                          fname, lineno, style );
                                    goto fail;
                              }

                              if ( b->a_authz.sai_transport_ssf ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "transport_ssf attribute already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( right == NULL || *right == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: no transport_ssf is defined.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( lutil_atou( &b->a_authz.sai_transport_ssf, right ) != 0 ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "unable to parse transport_ssf value (%s).\n",
                                          fname, lineno, right );
                                    goto fail;
                              }

                              if ( !b->a_authz.sai_transport_ssf ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: invalid transport_ssf value (%s).\n",
                                          fname, lineno, right );
                                    goto fail;
                              }
                              continue;
                        }

                        if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
                              if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause.\n",
                                          fname, lineno, style );
                                    goto fail;
                              }

                              if ( b->a_authz.sai_tls_ssf ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "tls_ssf attribute already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( right == NULL || *right == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: no tls_ssf is defined\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( lutil_atou( &b->a_authz.sai_tls_ssf, right ) != 0 ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "unable to parse tls_ssf value (%s).\n",
                                          fname, lineno, right );
                                    goto fail;
                              }

                              if ( !b->a_authz.sai_tls_ssf ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: invalid tls_ssf value (%s).\n",
                                          fname, lineno, right );
                                    goto fail;
                              }
                              continue;
                        }

                        if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
                              if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "inappropriate style \"%s\" in by clause.\n",
                                          fname, lineno, style );
                                    goto fail;
                              }

                              if ( b->a_authz.sai_sasl_ssf ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "sasl_ssf attribute already specified.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( right == NULL || *right == '\0' ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: no sasl_ssf is defined.\n",
                                          fname, lineno, 0 );
                                    goto fail;
                              }

                              if ( lutil_atou( &b->a_authz.sai_sasl_ssf, right ) != 0 ) {
                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                                          "unable to parse sasl_ssf value (%s).\n",
                                          fname, lineno, right );
                                    goto fail;
                              }

                              if ( !b->a_authz.sai_sasl_ssf ) {
                                    Debug( LDAP_DEBUG_ANY,
                                          "%s: line %d: invalid sasl_ssf value (%s).\n",
                                          fname, lineno, right );
                                    goto fail;
                              }
                              continue;
                        }

                        if ( right != NULL ) {
                              /* unsplit */
                              right[-1] = '=';
                        }
                        break;
                  }

                  if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) { 
                        /* out of arguments or plain stop */

                        ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
                        ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
                        b->a_type = ACL_STOP;

                        access_append( &a->acl_access, b );
                        continue;
                  }

                  if ( strcasecmp( left, "continue" ) == 0 ) {
                        /* plain continue */

                        ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
                        ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
                        b->a_type = ACL_CONTINUE;

                        access_append( &a->acl_access, b );
                        continue;
                  }

                  if ( strcasecmp( left, "break" ) == 0 ) {
                        /* plain continue */

                        ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
                        ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
                        b->a_type = ACL_BREAK;

                        access_append( &a->acl_access, b );
                        continue;
                  }

                  if ( strcasecmp( left, "by" ) == 0 ) {
                        /* we've gone too far */
                        --i;
                        ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
                        ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
                        b->a_type = ACL_STOP;

                        access_append( &a->acl_access, b );
                        continue;
                  }

                  /* get <access> */
                  {
                        char  *lleft = left;

                        if ( strncasecmp( left, "self", STRLENOF( "self" ) ) == 0 ) {
                              b->a_dn_self = 1;
                              lleft = &left[ STRLENOF( "self" ) ];

                        } else if ( strncasecmp( left, "realself", STRLENOF( "realself" ) ) == 0 ) {
                              b->a_realdn_self = 1;
                              lleft = &left[ STRLENOF( "realself" ) ];
                        }

                        ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( lleft ) );
                  }

                  if ( ACL_IS_INVALID( b->a_access_mask ) ) {
                        Debug( LDAP_DEBUG_ANY,
                              "%s: line %d: expecting <access> got \"%s\".\n",
                              fname, lineno, left );
                        goto fail;
                  }

                  b->a_type = ACL_STOP;

                  if ( ++i == argc ) {
                        /* out of arguments or plain stop */
                        access_append( &a->acl_access, b );
                        continue;
                  }

                  if ( strcasecmp( argv[i], "continue" ) == 0 ) {
                        /* plain continue */
                        b->a_type = ACL_CONTINUE;

                  } else if ( strcasecmp( argv[i], "break" ) == 0 ) {
                        /* plain continue */
                        b->a_type = ACL_BREAK;

                  } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
                        /* gone to far */
                        i--;
                  }

                  access_append( &a->acl_access, b );
                  b = NULL;

            } else {
                  Debug( LDAP_DEBUG_ANY,
                        "%s: line %d: expecting \"to\" "
                        "or \"by\" got \"%s\"\n",
                        fname, lineno, argv[i] );
                  goto fail;
            }
      }

      /* if we have no real access clause, complain and do nothing */
      if ( a == NULL ) {
            Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                  "warning: no access clause(s) specified in access line.\n",
                  fname, lineno, 0 );
            goto fail;

      } else {
#ifdef LDAP_DEBUG
            if ( slap_debug & LDAP_DEBUG_ACL ) {
                  print_acl( be, a );
            }
#endif
      
            if ( a->acl_access == NULL ) {
                  Debug( LDAP_DEBUG_ANY, "%s: line %d: "
                        "warning: no by clause(s) specified in access line.\n",
                        fname, lineno, 0 );
                  goto fail;
            }

            if ( be != NULL ) {
                  if ( be->be_nsuffix == NULL ) {
                        Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
                              "scope checking needs suffix before ACLs.\n",
                              fname, lineno, 0 );
                        /* go ahead, since checking is not authoritative */
                  } else if ( !BER_BVISNULL( &be->be_nsuffix[ 1 ] ) ) {
                        Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
                              "scope checking only applies to single-valued "
                              "suffix databases\n",
                              fname, lineno, 0 );
                        /* go ahead, since checking is not authoritative */
                  } else {
                        switch ( check_scope( be, a ) ) {
                        case ACL_SCOPE_UNKNOWN:
                              Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
                                    "cannot assess the validity of the ACL scope within "
                                    "backend naming context\n",
                                    fname, lineno, 0 );
                              break;

                        case ACL_SCOPE_WARN:
                              Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
                                    "ACL could be out of scope within backend naming context\n",
                                    fname, lineno, 0 );
                              break;

                        case ACL_SCOPE_PARTIAL:
                              Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
                                    "ACL appears to be partially out of scope within "
                                    "backend naming context\n",
                                    fname, lineno, 0 );
                              break;
      
                        case ACL_SCOPE_ERR:
                              Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
                                    "ACL appears to be out of scope within "
                                    "backend naming context\n",
                                    fname, lineno, 0 );
                              break;

                        default:
                              break;
                        }
                  }
                  acl_append( &be->be_acl, a, pos );

            } else {
                  acl_append( &frontendDB->be_acl, a, pos );
            }
      }

      return 0;

fail:
      if ( b ) access_free( b );
      if ( a ) acl_free( a );
      return acl_usage();
}

char *
accessmask2str( slap_mask_t mask, char *buf, int debug )
{
      int   none = 1;
      char  *ptr = buf;

      assert( buf != NULL );

      if ( ACL_IS_INVALID( mask ) ) {
            return "invalid";
      }

      buf[0] = '\0';

      if ( ACL_IS_LEVEL( mask ) ) {
            if ( ACL_LVL_IS_NONE(mask) ) {
                  ptr = lutil_strcopy( ptr, "none" );

            } else if ( ACL_LVL_IS_DISCLOSE(mask) ) {
                  ptr = lutil_strcopy( ptr, "disclose" );

            } else if ( ACL_LVL_IS_AUTH(mask) ) {
                  ptr = lutil_strcopy( ptr, "auth" );

            } else if ( ACL_LVL_IS_COMPARE(mask) ) {
                  ptr = lutil_strcopy( ptr, "compare" );

            } else if ( ACL_LVL_IS_SEARCH(mask) ) {
                  ptr = lutil_strcopy( ptr, "search" );

            } else if ( ACL_LVL_IS_READ(mask) ) {
                  ptr = lutil_strcopy( ptr, "read" );

            } else if ( ACL_LVL_IS_WRITE(mask) ) {
                  ptr = lutil_strcopy( ptr, "write" );

            } else if ( ACL_LVL_IS_WADD(mask) ) {
                  ptr = lutil_strcopy( ptr, "add" );

            } else if ( ACL_LVL_IS_WDEL(mask) ) {
                  ptr = lutil_strcopy( ptr, "delete" );

            } else if ( ACL_LVL_IS_MANAGE(mask) ) {
                  ptr = lutil_strcopy( ptr, "manage" );

            } else {
                  ptr = lutil_strcopy( ptr, "unknown" );
            }
            
            if ( !debug ) {
                  *ptr = '\0';
                  return buf;
            }
            *ptr++ = '(';
      }

      if( ACL_IS_ADDITIVE( mask ) ) {
            *ptr++ = '+';

      } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
            *ptr++ = '-';

      } else {
            *ptr++ = '=';
      }

      if ( ACL_PRIV_ISSET(mask, ACL_PRIV_MANAGE) ) {
            none = 0;
            *ptr++ = 'm';
      } 

      if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
            none = 0;
            *ptr++ = 'w';

      } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WADD) ) {
            none = 0;
            *ptr++ = 'a';

      } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) {
            none = 0;
            *ptr++ = 'z';
      } 

      if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
            none = 0;
            *ptr++ = 'r';
      } 

      if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
            none = 0;
            *ptr++ = 's';
      } 

      if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
            none = 0;
            *ptr++ = 'c';
      } 

      if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
            none = 0;
            *ptr++ = 'x';
      } 

      if ( ACL_PRIV_ISSET(mask, ACL_PRIV_DISCLOSE) ) {
            none = 0;
            *ptr++ = 'd';
      } 

      if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
            none = 0;
            *ptr++ = '0';
      } 

      if ( none ) {
            ptr = buf;
      }

      if ( ACL_IS_LEVEL( mask ) ) {
            *ptr++ = ')';
      }

      *ptr = '\0';

      return buf;
}

slap_mask_t
str2accessmask( const char *str )
{
      slap_mask_t mask;

      if( !ASCII_ALPHA(str[0]) ) {
            int i;

            if ( str[0] == '=' ) {
                  ACL_INIT(mask);

            } else if( str[0] == '+' ) {
                  ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);

            } else if( str[0] == '-' ) {
                  ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);

            } else {
                  ACL_INVALIDATE(mask);
                  return mask;
            }

            for( i=1; str[i] != '\0'; i++ ) {
                  if( TOLOWER((unsigned char) str[i]) == 'm' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_MANAGE);

                  } else if( TOLOWER((unsigned char) str[i]) == 'w' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_WRITE);

                  } else if( TOLOWER((unsigned char) str[i]) == 'a' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_WADD);

                  } else if( TOLOWER((unsigned char) str[i]) == 'z' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_WDEL);

                  } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_READ);

                  } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);

                  } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);

                  } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_AUTH);

                  } else if( TOLOWER((unsigned char) str[i]) == 'd' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);

                  } else if( str[i] == '0' ) {
                        ACL_PRIV_SET(mask, ACL_PRIV_NONE);

                  } else {
                        ACL_INVALIDATE(mask);
                        return mask;
                  }
            }

            return mask;
      }

      if ( strcasecmp( str, "none" ) == 0 ) {
            ACL_LVL_ASSIGN_NONE(mask);

      } else if ( strcasecmp( str, "disclose" ) == 0 ) {
            ACL_LVL_ASSIGN_DISCLOSE(mask);

      } else if ( strcasecmp( str, "auth" ) == 0 ) {
            ACL_LVL_ASSIGN_AUTH(mask);

      } else if ( strcasecmp( str, "compare" ) == 0 ) {
            ACL_LVL_ASSIGN_COMPARE(mask);

      } else if ( strcasecmp( str, "search" ) == 0 ) {
            ACL_LVL_ASSIGN_SEARCH(mask);

      } else if ( strcasecmp( str, "read" ) == 0 ) {
            ACL_LVL_ASSIGN_READ(mask);

      } else if ( strcasecmp( str, "add" ) == 0 ) {
            ACL_LVL_ASSIGN_WADD(mask);

      } else if ( strcasecmp( str, "delete" ) == 0 ) {
            ACL_LVL_ASSIGN_WDEL(mask);

      } else if ( strcasecmp( str, "write" ) == 0 ) {
            ACL_LVL_ASSIGN_WRITE(mask);

      } else if ( strcasecmp( str, "manage" ) == 0 ) {
            ACL_LVL_ASSIGN_MANAGE(mask);

      } else {
            ACL_INVALIDATE( mask );
      }

      return mask;
}

static int
acl_usage( void )
{
      char *access =
            "<access clause> ::= access to <what> "
                        "[ by <who> [ <access> ] [ <control> ] ]+ \n";
      char *what =
            "<what> ::= * | dn[.<dnstyle>=<DN>] [filter=<filter>] [attrs=<attrspec>]\n"
            "<attrspec> ::= <attrname> [val[/<matchingRule>][.<attrstyle>]=<value>] | <attrlist>\n"
            "<attrlist> ::= <attr> [ , <attrlist> ]\n"
            "<attr> ::= <attrname> | @<objectClass> | !<objectClass> | entry | children\n";

      char *who =
            "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
                  "\t[ realanonymous | realusers | realself | realdn[.<dnstyle>]=<DN> ]\n"
                  "\t[dnattr=<attrname>]\n"
                  "\t[realdnattr=<attrname>]\n"
                  "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
                  "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n"
                  "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
#ifdef SLAP_DYNACL
                  "\t[dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]]\n"
#endif /* SLAP_DYNACL */
                  "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
            "<style> ::= exact | regex | base(Object)\n"
            "<dnstyle> ::= base(Object) | one(level) | sub(tree) | children | "
                  "exact | regex\n"
            "<attrstyle> ::= exact | regex | base(Object) | one(level) | "
                  "sub(tree) | children\n"
            "<peernamestyle> ::= exact | regex | ip | ipv6 | path\n"
            "<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n"
            "<access> ::= [[real]self]{<level>|<priv>}\n"
            "<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n"
            "<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n"
            "<control> ::= [ stop | continue | break ]\n"
#ifdef SLAP_DYNACL
#ifdef SLAPD_ACI_ENABLED
            "dynacl:\n"
            "\t<name>=ACI\t<pattern>=<attrname>\n"
#endif /* SLAPD_ACI_ENABLED */
#endif /* ! SLAP_DYNACL */
            "";

      Debug( LDAP_DEBUG_ANY, "%s%s%s\n", access, what, who );

      return 1;
}

/*
 * Set pattern to a "normalized" DN from src.
 * At present it simply eats the (optional) space after 
 * a RDN separator (,)
 * Eventually will evolve in a more complete normalization
 */
static void
acl_regex_normalized_dn(
      const char *src,
      struct berval *pattern )
{
      char *str, *p;
      ber_len_t len;

      str = ch_strdup( src );
      len = strlen( src );

      for ( p = str; p && p[0]; p++ ) {
            /* escape */
            if ( p[0] == '\\' && p[1] ) {
                  /* 
                   * if escaping a hex pair we should
                   * increment p twice; however, in that 
                   * case the second hex number does 
                   * no harm
                   */
                  p++;
            }

            if ( p[0] == ',' && p[1] == ' ' ) {
                  char *q;
                  
                  /*
                   * too much space should be an error if we are pedantic
                   */
                  for ( q = &p[2]; q[0] == ' '; q++ ) {
                        /* DO NOTHING */ ;
                  }
                  AC_MEMCPY( p+1, q, len-(q-str)+1);
            }
      }
      pattern->bv_val = str;
      pattern->bv_len = p - str;

      return;
}

static void
split(
    char    *line,
    int           splitchar,
    char    **left,
    char    **right )
{
      *left = line;
      if ( (*right = strchr( line, splitchar )) != NULL ) {
            *((*right)++) = '\0';
      }
}

static void
access_append( Access **l, Access *a )
{
      for ( ; *l != NULL; l = &(*l)->a_next ) {
            ;     /* Empty */
      }

      *l = a;
}

void
acl_append( AccessControl **l, AccessControl *a, int pos )
{
      int i;

      for (i=0 ; i != pos && *l != NULL; l = &(*l)->acl_next, i++ ) {
            ;     /* Empty */
      }
      if ( *l && a )
            a->acl_next = *l;
      *l = a;
}

static void
access_free( Access *a )
{
      if ( !BER_BVISNULL( &a->a_dn_pat ) ) {
            free( a->a_dn_pat.bv_val );
      }
      if ( !BER_BVISNULL( &a->a_realdn_pat ) ) {
            free( a->a_realdn_pat.bv_val );
      }
      if ( !BER_BVISNULL( &a->a_peername_pat ) ) {
            free( a->a_peername_pat.bv_val );
      }
      if ( !BER_BVISNULL( &a->a_sockname_pat ) ) {
            free( a->a_sockname_pat.bv_val );
      }
      if ( !BER_BVISNULL( &a->a_domain_pat ) ) {
            free( a->a_domain_pat.bv_val );
      }
      if ( !BER_BVISNULL( &a->a_sockurl_pat ) ) {
            free( a->a_sockurl_pat.bv_val );
      }
      if ( !BER_BVISNULL( &a->a_set_pat ) ) {
            free( a->a_set_pat.bv_val );
      }
      if ( !BER_BVISNULL( &a->a_group_pat ) ) {
            free( a->a_group_pat.bv_val );
      }
#ifdef SLAP_DYNACL
      if ( a->a_dynacl != NULL ) {
            slap_dynacl_t     *da;
            for ( da = a->a_dynacl; da; ) {
                  slap_dynacl_t     *tmp = da;

                  da = da->da_next;

                  if ( tmp->da_destroy ) {
                        tmp->da_destroy( tmp->da_private );
                  }

                  ch_free( tmp );
            }
      }
#endif /* SLAP_DYNACL */
      free( a );
}

void
acl_free( AccessControl *a )
{
      Access *n;
      AttributeName *an;

      if ( a->acl_filter ) {
            filter_free( a->acl_filter );
      }
      if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
            if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
                  regfree( &a->acl_dn_re );
            }
            free ( a->acl_dn_pat.bv_val );
      }
      if ( a->acl_attrs ) {
            for ( an = a->acl_attrs; !BER_BVISNULL( &an->an_name ); an++ ) {
                  free( an->an_name.bv_val );
            }
            free( a->acl_attrs );

            if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
                  regfree( &a->acl_attrval_re );
            }

            if ( !BER_BVISNULL( &a->acl_attrval ) ) {
                  ber_memfree( a->acl_attrval.bv_val );
            }
      }
      for ( ; a->acl_access; a->acl_access = n ) {
            n = a->acl_access->a_next;
            access_free( a->acl_access );
      }
      free( a );
}

/* Because backend_startup uses acl_append to tack on the global_acl to
 * the end of each backend's acl, we cannot just take one argument and
 * merrily free our way to the end of the list. backend_destroy calls us
 * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
 * point. config_destroy calls us with global_acl in arg1 and NULL in
 * arg2, so we then proceed to polish off the global_acl.
 */
void
acl_destroy( AccessControl *a, AccessControl *end )
{
      AccessControl *n;

      for ( ; a && a != end; a = n ) {
            n = a->acl_next;
            acl_free( a );
      }
}

char *
access2str( slap_access_t access )
{
      if ( access == ACL_NONE ) {
            return "none";

      } else if ( access == ACL_DISCLOSE ) {
            return "disclose";

      } else if ( access == ACL_AUTH ) {
            return "auth";

      } else if ( access == ACL_COMPARE ) {
            return "compare";

      } else if ( access == ACL_SEARCH ) {
            return "search";

      } else if ( access == ACL_READ ) {
            return "read";

      } else if ( access == ACL_WRITE ) {
            return "write";

      } else if ( access == ACL_WADD ) {
            return "add";

      } else if ( access == ACL_WDEL ) {
            return "delete";

      } else if ( access == ACL_MANAGE ) {
            return "manage";

      }

      return "unknown";
}

slap_access_t
str2access( const char *str )
{
      if ( strcasecmp( str, "none" ) == 0 ) {
            return ACL_NONE;

      } else if ( strcasecmp( str, "disclose" ) == 0 ) {
            return ACL_DISCLOSE;

      } else if ( strcasecmp( str, "auth" ) == 0 ) {
            return ACL_AUTH;

      } else if ( strcasecmp( str, "compare" ) == 0 ) {
            return ACL_COMPARE;

      } else if ( strcasecmp( str, "search" ) == 0 ) {
            return ACL_SEARCH;

      } else if ( strcasecmp( str, "read" ) == 0 ) {
            return ACL_READ;

      } else if ( strcasecmp( str, "write" ) == 0 ) {
            return ACL_WRITE;

      } else if ( strcasecmp( str, "add" ) == 0 ) {
            return ACL_WADD;

      } else if ( strcasecmp( str, "delete" ) == 0 ) {
            return ACL_WDEL;

      } else if ( strcasecmp( str, "manage" ) == 0 ) {
            return ACL_MANAGE;
      }

      return( ACL_INVALID_ACCESS );
}

#define ACLBUF_MAXLEN   8192

static char aclbuf[ACLBUF_MAXLEN];

static char *
dnaccess2text( slap_dn_access *bdn, char *ptr, int is_realdn )
{
      *ptr++ = ' ';

      if ( is_realdn ) {
            ptr = lutil_strcopy( ptr, "real" );
      }

      if ( ber_bvccmp( &bdn->a_pat, '*' ) ||
            bdn->a_style == ACL_STYLE_ANONYMOUS ||
            bdn->a_style == ACL_STYLE_USERS ||
            bdn->a_style == ACL_STYLE_SELF )
      {
            if ( is_realdn ) {
                  assert( ! ber_bvccmp( &bdn->a_pat, '*' ) );
            }
                  
            ptr = lutil_strcopy( ptr, bdn->a_pat.bv_val );
            if ( bdn->a_style == ACL_STYLE_SELF && bdn->a_self_level != 0 ) {
                  int n = sprintf( ptr, ".level{%d}", bdn->a_self_level );
                  if ( n > 0 ) {
                        ptr += n;
                  } /* else ? */
            }

      } else {
            ptr = lutil_strcopy( ptr, "dn." );
            if ( bdn->a_style == ACL_STYLE_BASE )
                  ptr = lutil_strcopy( ptr, style_base );
            else 
                  ptr = lutil_strcopy( ptr, style_strings[bdn->a_style] );
            if ( bdn->a_style == ACL_STYLE_LEVEL ) {
                  int n = sprintf( ptr, "{%d}", bdn->a_level );
                  if ( n > 0 ) {
                        ptr += n;
                  } /* else ? */
            }
            if ( bdn->a_expand ) {
                  ptr = lutil_strcopy( ptr, ",expand" );
            }
            *ptr++ = '=';
            *ptr++ = '"';
            ptr = lutil_strcopy( ptr, bdn->a_pat.bv_val );
            *ptr++ = '"';
      }
      return ptr;
}

static char *
access2text( Access *b, char *ptr )
{
      char maskbuf[ACCESSMASK_MAXLEN];

      ptr = lutil_strcopy( ptr, "\tby" );

      if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
            ptr = dnaccess2text( &b->a_dn, ptr, 0 );
      }
      if ( b->a_dn_at ) {
            ptr = lutil_strcopy( ptr, " dnattr=" );
            ptr = lutil_strcopy( ptr, b->a_dn_at->ad_cname.bv_val );
      }

      if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
            ptr = dnaccess2text( &b->a_realdn, ptr, 1 );
      }
      if ( b->a_realdn_at ) {
            ptr = lutil_strcopy( ptr, " realdnattr=" );
            ptr = lutil_strcopy( ptr, b->a_realdn_at->ad_cname.bv_val );
      }

      if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
            ptr = lutil_strcopy( ptr, " group/" );
            ptr = lutil_strcopy( ptr, b->a_group_oc ?
                  b->a_group_oc->soc_cname.bv_val : SLAPD_GROUP_CLASS );
            *ptr++ = '/';
            ptr = lutil_strcopy( ptr, b->a_group_at ?
                  b->a_group_at->ad_cname.bv_val : SLAPD_GROUP_ATTR );
            *ptr++ = '.';
            ptr = lutil_strcopy( ptr, style_strings[b->a_group_style] );
            *ptr++ = '=';
            *ptr++ = '"';
            ptr = lutil_strcopy( ptr, b->a_group_pat.bv_val );
            *ptr++ = '"';
      }

      if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
            ptr = lutil_strcopy( ptr, " peername" );
            *ptr++ = '.';
            ptr = lutil_strcopy( ptr, style_strings[b->a_peername_style] );
            *ptr++ = '=';
            *ptr++ = '"';
            ptr = lutil_strcopy( ptr, b->a_peername_pat.bv_val );
            *ptr++ = '"';
      }

      if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
            ptr = lutil_strcopy( ptr, " sockname" );
            *ptr++ = '.';
            ptr = lutil_strcopy( ptr, style_strings[b->a_sockname_style] );
            *ptr++ = '=';
            *ptr++ = '"';
            ptr = lutil_strcopy( ptr, b->a_sockname_pat.bv_val );
            *ptr++ = '"';
      }

      if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
            ptr = lutil_strcopy( ptr, " domain" );
            *ptr++ = '.';
            ptr = lutil_strcopy( ptr, style_strings[b->a_domain_style] );
            if ( b->a_domain_expand ) {
                  ptr = lutil_strcopy( ptr, ",expand" );
            }
            *ptr++ = '=';
            ptr = lutil_strcopy( ptr, b->a_domain_pat.bv_val );
      }

      if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
            ptr = lutil_strcopy( ptr, " sockurl" );
            *ptr++ = '.';
            ptr = lutil_strcopy( ptr, style_strings[b->a_sockurl_style] );
            *ptr++ = '=';
            *ptr++ = '"';
            ptr = lutil_strcopy( ptr, b->a_sockurl_pat.bv_val );
            *ptr++ = '"';
      }

      if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
            ptr = lutil_strcopy( ptr, " set" );
            *ptr++ = '.';
            ptr = lutil_strcopy( ptr, style_strings[b->a_set_style] );
            *ptr++ = '=';
            *ptr++ = '"';
            ptr = lutil_strcopy( ptr, b->a_set_pat.bv_val );
            *ptr++ = '"';
      }

#ifdef SLAP_DYNACL
      if ( b->a_dynacl ) {
            slap_dynacl_t     *da;

            for ( da = b->a_dynacl; da; da = da->da_next ) {
                  if ( da->da_unparse ) {
                        struct berval bv = BER_BVNULL;
                        (void)( *da->da_unparse )( da->da_private, &bv );
                        assert( !BER_BVISNULL( &bv ) );
                        ptr = lutil_strcopy( ptr, bv.bv_val );
                        ch_free( bv.bv_val );
                  }
            }
      }
#endif /* SLAP_DYNACL */

      /* Security Strength Factors */
      if ( b->a_authz.sai_ssf ) {
            ptr += sprintf( ptr, " ssf=%u", 
                  b->a_authz.sai_ssf );
      }
      if ( b->a_authz.sai_transport_ssf ) {
            ptr += sprintf( ptr, " transport_ssf=%u",
                  b->a_authz.sai_transport_ssf );
      }
      if ( b->a_authz.sai_tls_ssf ) {
            ptr += sprintf( ptr, " tls_ssf=%u",
                  b->a_authz.sai_tls_ssf );
      }
      if ( b->a_authz.sai_sasl_ssf ) {
            ptr += sprintf( ptr, " sasl_ssf=%u",
                  b->a_authz.sai_sasl_ssf );
      }

      *ptr++ = ' ';
      if ( b->a_dn_self ) {
            ptr = lutil_strcopy( ptr, "self" );
      } else if ( b->a_realdn_self ) {
            ptr = lutil_strcopy( ptr, "realself" );
      }
      ptr = lutil_strcopy( ptr, accessmask2str( b->a_access_mask, maskbuf, 0 ));
      if ( !maskbuf[0] ) ptr--;

      if( b->a_type == ACL_BREAK ) {
            ptr = lutil_strcopy( ptr, " break" );

      } else if( b->a_type == ACL_CONTINUE ) {
            ptr = lutil_strcopy( ptr, " continue" );

      } else if( b->a_type != ACL_STOP ) {
            ptr = lutil_strcopy( ptr, " unknown-control" );
      } else {
            if ( !maskbuf[0] ) ptr = lutil_strcopy( ptr, " stop" );
      }
      *ptr++ = '\n';

      return ptr;
}

void
acl_unparse( AccessControl *a, struct berval *bv )
{
      Access      *b;
      char  *ptr;
      int   to = 0;

      bv->bv_val = aclbuf;
      bv->bv_len = 0;

      ptr = bv->bv_val;

      ptr = lutil_strcopy( ptr, "to" );
      if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
            to++;
            ptr = lutil_strcopy( ptr, " dn." );
            if ( a->acl_dn_style == ACL_STYLE_BASE )
                  ptr = lutil_strcopy( ptr, style_base );
            else
                  ptr = lutil_strcopy( ptr, style_strings[a->acl_dn_style] );
            *ptr++ = '=';
            *ptr++ = '"';
            ptr = lutil_strcopy( ptr, a->acl_dn_pat.bv_val );
            ptr = lutil_strcopy( ptr, "\"\n" );
      }

      if ( a->acl_filter != NULL ) {
            struct berval     bv = BER_BVNULL;

            to++;
            filter2bv( a->acl_filter, &bv );
            ptr = lutil_strcopy( ptr, " filter=\"" );
            ptr = lutil_strcopy( ptr, bv.bv_val );
            *ptr++ = '"';
            *ptr++ = '\n';
            ch_free( bv.bv_val );
      }

      if ( a->acl_attrs != NULL ) {
            int   first = 1;
            AttributeName *an;
            to++;

            ptr = lutil_strcopy( ptr, " attrs=" );
            for ( an = a->acl_attrs; an && !BER_BVISNULL( &an->an_name ); an++ ) {
                  if ( ! first ) *ptr++ = ',';
                  if (an->an_oc) {
                        *ptr++ = an->an_oc_exclude ? '!' : '@';
                        ptr = lutil_strcopy( ptr, an->an_oc->soc_cname.bv_val );

                  } else {
                        ptr = lutil_strcopy( ptr, an->an_name.bv_val );
                  }
                  first = 0;
            }
            *ptr++ = '\n';
      }

      if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
            to++;
            ptr = lutil_strcopy( ptr, " val." );
            if ( a->acl_attrval_style == ACL_STYLE_BASE &&
                  a->acl_attrs[0].an_desc->ad_type->sat_syntax ==
                        slap_schema.si_syn_distinguishedName )
                  ptr = lutil_strcopy( ptr, style_base );
            else
                  ptr = lutil_strcopy( ptr, style_strings[a->acl_attrval_style] );
            *ptr++ = '=';
            *ptr++ = '"';
            ptr = lutil_strcopy( ptr, a->acl_attrval.bv_val );
            *ptr++ = '"';
            *ptr++ = '\n';
      }

      if( !to ) {
            ptr = lutil_strcopy( ptr, " *\n" );
      }

      for ( b = a->acl_access; b != NULL; b = b->a_next ) {
            ptr = access2text( b, ptr );
      }
      *ptr = '\0';
      bv->bv_len = ptr - bv->bv_val;
}

#ifdef LDAP_DEBUG
static void
print_acl( Backend *be, AccessControl *a )
{
      struct berval bv;

      acl_unparse( a, &bv );
      fprintf( stderr, "%s ACL: access %s\n",
            be == NULL ? "Global" : "Backend", bv.bv_val );
}
#endif /* LDAP_DEBUG */

Generated by  Doxygen 1.6.0   Back to index