Back to index

openldap  2.4.31
aclparse.c
Go to the documentation of this file.
00001 /* aclparse.c - routines to parse and check acl's */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1998-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
00017  * All rights reserved.
00018  *
00019  * Redistribution and use in source and binary forms are permitted
00020  * provided that this notice is preserved and that due credit is given
00021  * to the University of Michigan at Ann Arbor. The name of the University
00022  * may not be used to endorse or promote products derived from this
00023  * software without specific prior written permission. This software
00024  * is provided ``as is'' without express or implied warranty.
00025  */
00026 
00027 #include "portable.h"
00028 
00029 #include <stdio.h>
00030 
00031 #include <ac/ctype.h>
00032 #include <ac/regex.h>
00033 #include <ac/socket.h>
00034 #include <ac/string.h>
00035 #include <ac/unistd.h>
00036 
00037 #include "slap.h"
00038 #include "lber_pvt.h"
00039 #include "lutil.h"
00040 
00041 static const char style_base[] = "base";
00042 const char *style_strings[] = {
00043        "regex",
00044        "expand",
00045        "exact",
00046        "one",
00047        "subtree",
00048        "children",
00049        "level",
00050        "attrof",
00051        "anonymous",
00052        "users",
00053        "self",
00054        "ip",
00055        "ipv6",
00056        "path",
00057        NULL
00058 };
00059 
00060 #define ACLBUF_CHUNKSIZE    8192
00061 static struct berval aclbuf;
00062 
00063 static void          split(char *line, int splitchar, char **left, char **right);
00064 static void          access_append(Access **l, Access *a);
00065 static void          access_free( Access *a );
00066 static int           acl_usage(void);
00067 
00068 static void          acl_regex_normalized_dn(const char *src, struct berval *pat);
00069 
00070 #ifdef LDAP_DEBUG
00071 static void          print_acl(Backend *be, AccessControl *a);
00072 #endif
00073 
00074 static int           check_scope( BackendDB *be, AccessControl *a );
00075 
00076 #ifdef SLAP_DYNACL
00077 static int
00078 slap_dynacl_config(
00079        const char *fname,
00080        int lineno,
00081        Access *b,
00082        const char *name,
00083        const char *opts,
00084        slap_style_t sty,
00085        const char *right )
00086 {
00087        slap_dynacl_t *da, *tmp;
00088        int           rc = 0;
00089 
00090        for ( da = b->a_dynacl; da; da = da->da_next ) {
00091               if ( strcasecmp( da->da_name, name ) == 0 ) {
00092                      Debug( LDAP_DEBUG_ANY,
00093                             "%s: line %d: dynacl \"%s\" already specified.\n",
00094                             fname, lineno, name );
00095                      return acl_usage();
00096               }
00097        }
00098 
00099        da = slap_dynacl_get( name );
00100        if ( da == NULL ) {
00101               return -1;
00102        }
00103 
00104        tmp = ch_malloc( sizeof( slap_dynacl_t ) );
00105        *tmp = *da;
00106 
00107        if ( tmp->da_parse ) {
00108               rc = ( *tmp->da_parse )( fname, lineno, opts, sty, right, &tmp->da_private );
00109               if ( rc ) {
00110                      ch_free( tmp );
00111                      return rc;
00112               }
00113        }
00114 
00115        tmp->da_next = b->a_dynacl;
00116        b->a_dynacl = tmp;
00117 
00118        return 0;
00119 }
00120 #endif /* SLAP_DYNACL */
00121 
00122 static void
00123 regtest(const char *fname, int lineno, char *pat) {
00124        int e;
00125        regex_t re;
00126 
00127        char          buf[ SLAP_TEXT_BUFLEN ];
00128        unsigned      size;
00129 
00130        char *sp;
00131        char *dp;
00132        int  flag;
00133 
00134        sp = pat;
00135        dp = buf;
00136        size = 0;
00137        buf[0] = '\0';
00138 
00139        for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
00140               if (flag) {
00141                      if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
00142                             *dp++ = *sp;
00143                             size++;
00144                      }
00145                      flag = 0;
00146 
00147               } else {
00148                      if (*sp == '$') {
00149                             flag = 1;
00150                      } else {
00151                             *dp++ = *sp;
00152                             size++;
00153                      }
00154               }
00155        }
00156 
00157        *dp = '\0';
00158        if ( size >= (sizeof(buf) - 1) ) {
00159               Debug( LDAP_DEBUG_ANY,
00160                      "%s: line %d: regular expression \"%s\" too large\n",
00161                      fname, lineno, pat );
00162               (void)acl_usage();
00163               exit( EXIT_FAILURE );
00164        }
00165 
00166        if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
00167               char error[ SLAP_TEXT_BUFLEN ];
00168 
00169               regerror(e, &re, error, sizeof(error));
00170 
00171               snprintf( buf, sizeof( buf ),
00172                      "regular expression \"%s\" bad because of %s",
00173                      pat, error );
00174               Debug( LDAP_DEBUG_ANY,
00175                      "%s: line %d: %s\n",
00176                      fname, lineno, buf );
00177               acl_usage();
00178               exit( EXIT_FAILURE );
00179        }
00180        regfree(&re);
00181 }
00182 
00183 /*
00184  * Experimental
00185  *
00186  * Check if the pattern of an ACL, if any, matches the scope
00187  * of the backend it is defined within.
00188  */
00189 #define       ACL_SCOPE_UNKNOWN    (-2)
00190 #define       ACL_SCOPE_ERR        (-1)
00191 #define       ACL_SCOPE_OK         (0)
00192 #define       ACL_SCOPE_PARTIAL    (1)
00193 #define       ACL_SCOPE_WARN              (2)
00194 
00195 static int
00196 check_scope( BackendDB *be, AccessControl *a )
00197 {
00198        ber_len_t     patlen;
00199        struct berval dn;
00200 
00201        dn = be->be_nsuffix[0];
00202 
00203        if ( BER_BVISEMPTY( &dn ) ) {
00204               return ACL_SCOPE_OK;
00205        }
00206 
00207        if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
00208                      a->acl_dn_style != ACL_STYLE_REGEX )
00209        {
00210               slap_style_t  style = a->acl_dn_style;
00211 
00212               if ( style == ACL_STYLE_REGEX ) {
00213                      char          dnbuf[SLAP_LDAPDN_MAXLEN + 2];
00214                      char          rebuf[SLAP_LDAPDN_MAXLEN + 1];
00215                      ber_len_t     rebuflen;
00216                      regex_t              re;
00217                      int           rc;
00218                      
00219                      /* add trailing '$' to database suffix to form
00220                       * a simple trial regex pattern "<suffix>$" */
00221                      AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
00222                             be->be_nsuffix[0].bv_len );
00223                      dnbuf[be->be_nsuffix[0].bv_len] = '$';
00224                      dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';
00225 
00226                      if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
00227                             return ACL_SCOPE_WARN;
00228                      }
00229 
00230                      /* remove trailing ')$', if any, from original
00231                       * regex pattern */
00232                      rebuflen = a->acl_dn_pat.bv_len;
00233                      AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
00234                      if ( rebuf[rebuflen - 1] == '$' ) {
00235                             rebuf[--rebuflen] = '\0';
00236                      }
00237                      while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
00238                             rebuf[--rebuflen] = '\0';
00239                      }
00240                      if ( rebuflen == be->be_nsuffix[0].bv_len ) {
00241                             rc = ACL_SCOPE_WARN;
00242                             goto regex_done;
00243                      }
00244 
00245                      /* not a clear indication of scoping error, though */
00246                      rc = regexec( &re, rebuf, 0, NULL, 0 )
00247                             ? ACL_SCOPE_WARN : ACL_SCOPE_OK;
00248 
00249 regex_done:;
00250                      regfree( &re );
00251                      return rc;
00252               }
00253 
00254               patlen = a->acl_dn_pat.bv_len;
00255               /* If backend suffix is longer than pattern,
00256                * it is a potential mismatch (in the sense
00257                * that a superior naming context could
00258                * match */
00259               if ( dn.bv_len > patlen ) {
00260                      /* base is blatantly wrong */
00261                      if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;
00262 
00263                      /* a style of one can be wrong if there is
00264                       * more than one level between the suffix
00265                       * and the pattern */
00266                      if ( style == ACL_STYLE_ONE ) {
00267                             ber_len_t     rdnlen = 0;
00268                             int           sep = 0;
00269 
00270                             if ( patlen > 0 ) {
00271                                    if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
00272                                           return ACL_SCOPE_ERR;
00273                                    }
00274                                    sep = 1;
00275                             }
00276 
00277                             rdnlen = dn_rdnlen( NULL, &dn );
00278                             if ( rdnlen != dn.bv_len - patlen - sep )
00279                                    return ACL_SCOPE_ERR;
00280                      }
00281 
00282                      /* if the trailing part doesn't match,
00283                       * then it's an error */
00284                      if ( strcmp( a->acl_dn_pat.bv_val,
00285                             &dn.bv_val[dn.bv_len - patlen] ) != 0 )
00286                      {
00287                             return ACL_SCOPE_ERR;
00288                      }
00289 
00290                      return ACL_SCOPE_PARTIAL;
00291               }
00292 
00293               switch ( style ) {
00294               case ACL_STYLE_BASE:
00295               case ACL_STYLE_ONE:
00296               case ACL_STYLE_CHILDREN:
00297               case ACL_STYLE_SUBTREE:
00298                      break;
00299 
00300               default:
00301                      assert( 0 );
00302                      break;
00303               }
00304 
00305               if ( dn.bv_len < patlen &&
00306                      !DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
00307               {
00308                      return ACL_SCOPE_ERR;
00309               }
00310 
00311               if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
00312                      != 0 )
00313               {
00314                      return ACL_SCOPE_ERR;
00315               }
00316 
00317               return ACL_SCOPE_OK;
00318        }
00319 
00320        return ACL_SCOPE_UNKNOWN;
00321 }
00322 
00323 int
00324 parse_acl(
00325        Backend       *be,
00326        const char    *fname,
00327        int           lineno,
00328        int           argc,
00329        char          **argv,
00330        int           pos )
00331 {
00332        int           i;
00333        char          *left, *right, *style;
00334        struct berval bv;
00335        AccessControl *a = NULL;
00336        Access *b = NULL;
00337        int rc;
00338        const char *text;
00339 
00340        for ( i = 1; i < argc; i++ ) {
00341               /* to clause - select which entries are protected */
00342               if ( strcasecmp( argv[i], "to" ) == 0 ) {
00343                      if ( a != NULL ) {
00344                             Debug( LDAP_DEBUG_ANY, "%s: line %d: "
00345                                    "only one to clause allowed in access line\n",
00346                                 fname, lineno, 0 );
00347                             goto fail;
00348                      }
00349                      a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
00350                      a->acl_attrval_style = ACL_STYLE_NONE;
00351                      for ( ++i; i < argc; i++ ) {
00352                             if ( strcasecmp( argv[i], "by" ) == 0 ) {
00353                                    i--;
00354                                    break;
00355                             }
00356 
00357                             if ( strcasecmp( argv[i], "*" ) == 0 ) {
00358                                    if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
00359                                           a->acl_dn_style != ACL_STYLE_REGEX )
00360                                    {
00361                                           Debug( LDAP_DEBUG_ANY,
00362                                                  "%s: line %d: dn pattern"
00363                                                  " already specified in to clause.\n",
00364                                                  fname, lineno, 0 );
00365                                           goto fail;
00366                                    }
00367 
00368                                    ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
00369                                    continue;
00370                             }
00371 
00372                             split( argv[i], '=', &left, &right );
00373                             split( left, '.', &left, &style );
00374 
00375                             if ( right == NULL ) {
00376                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
00377                                           "missing \"=\" in \"%s\" in to clause\n",
00378                                        fname, lineno, left );
00379                                    goto fail;
00380                             }
00381 
00382                             if ( strcasecmp( left, "dn" ) == 0 ) {
00383                                    if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
00384                                           a->acl_dn_style != ACL_STYLE_REGEX )
00385                                    {
00386                                           Debug( LDAP_DEBUG_ANY,
00387                                                  "%s: line %d: dn pattern"
00388                                                  " already specified in to clause.\n",
00389                                                  fname, lineno, 0 );
00390                                           goto fail;
00391                                    }
00392 
00393                                    if ( style == NULL || *style == '\0' ||
00394                                           strcasecmp( style, "baseObject" ) == 0 ||
00395                                           strcasecmp( style, "base" ) == 0 ||
00396                                           strcasecmp( style, "exact" ) == 0 )
00397                                    {
00398                                           a->acl_dn_style = ACL_STYLE_BASE;
00399                                           ber_str2bv( right, 0, 1, &a->acl_dn_pat );
00400 
00401                                    } else if ( strcasecmp( style, "oneLevel" ) == 0 ||
00402                                           strcasecmp( style, "one" ) == 0 )
00403                                    {
00404                                           a->acl_dn_style = ACL_STYLE_ONE;
00405                                           ber_str2bv( right, 0, 1, &a->acl_dn_pat );
00406 
00407                                    } else if ( strcasecmp( style, "subtree" ) == 0 ||
00408                                           strcasecmp( style, "sub" ) == 0 )
00409                                    {
00410                                           if( *right == '\0' ) {
00411                                                  ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
00412 
00413                                           } else {
00414                                                  a->acl_dn_style = ACL_STYLE_SUBTREE;
00415                                                  ber_str2bv( right, 0, 1, &a->acl_dn_pat );
00416                                           }
00417 
00418                                    } else if ( strcasecmp( style, "children" ) == 0 ) {
00419                                           a->acl_dn_style = ACL_STYLE_CHILDREN;
00420                                           ber_str2bv( right, 0, 1, &a->acl_dn_pat );
00421 
00422                                    } else if ( strcasecmp( style, "regex" ) == 0 ) {
00423                                           a->acl_dn_style = ACL_STYLE_REGEX;
00424 
00425                                           if ( *right == '\0' ) {
00426                                                  /* empty regex should match empty DN */
00427                                                  a->acl_dn_style = ACL_STYLE_BASE;
00428                                                  ber_str2bv( right, 0, 1, &a->acl_dn_pat );
00429 
00430                                           } else if ( strcmp(right, "*") == 0 
00431                                                  || strcmp(right, ".*") == 0 
00432                                                  || strcmp(right, ".*$") == 0 
00433                                                  || strcmp(right, "^.*") == 0 
00434                                                  || strcmp(right, "^.*$") == 0
00435                                                  || strcmp(right, ".*$$") == 0 
00436                                                  || strcmp(right, "^.*$$") == 0 )
00437                                           {
00438                                                  ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
00439 
00440                                           } else {
00441                                                  acl_regex_normalized_dn( right, &a->acl_dn_pat );
00442                                           }
00443 
00444                                    } else {
00445                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
00446                                                  "unknown dn style \"%s\" in to clause\n",
00447                                               fname, lineno, style );
00448                                           goto fail;
00449                                    }
00450 
00451                                    continue;
00452                             }
00453 
00454                             if ( strcasecmp( left, "filter" ) == 0 ) {
00455                                    if ( (a->acl_filter = str2filter( right )) == NULL ) {
00456                                           Debug( LDAP_DEBUG_ANY,
00457                             "%s: line %d: bad filter \"%s\" in to clause\n",
00458                                               fname, lineno, right );
00459                                           goto fail;
00460                                    }
00461 
00462                             } else if ( strcasecmp( left, "attr" ) == 0             /* TOLERATED */
00463                                           || strcasecmp( left, "attrs" ) == 0 )     /* DOCUMENTED */
00464                             {
00465                                    if ( strcasecmp( left, "attr" ) == 0 ) {
00466                                           Debug( LDAP_DEBUG_ANY,
00467                                                  "%s: line %d: \"attr\" "
00468                                                  "is deprecated (and undocumented); "
00469                                                  "use \"attrs\" instead.\n",
00470                                                  fname, lineno, 0 );
00471                                    }
00472 
00473                                    a->acl_attrs = str2anlist( a->acl_attrs,
00474                                           right, "," );
00475                                    if ( a->acl_attrs == NULL ) {
00476                                           Debug( LDAP_DEBUG_ANY,
00477                             "%s: line %d: unknown attr \"%s\" in to clause\n",
00478                                               fname, lineno, right );
00479                                           goto fail;
00480                                    }
00481 
00482                             } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
00483                                    struct berval bv;
00484                                    char          *mr;
00485                                    
00486                                    if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
00487                                           Debug( LDAP_DEBUG_ANY,
00488                             "%s: line %d: attr val already specified in to clause.\n",
00489                                                  fname, lineno, 0 );
00490                                           goto fail;
00491                                    }
00492                                    if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
00493                                    {
00494                                           Debug( LDAP_DEBUG_ANY,
00495                             "%s: line %d: attr val requires a single attribute.\n",
00496                                                  fname, lineno, 0 );
00497                                           goto fail;
00498                                    }
00499 
00500                                    ber_str2bv( right, 0, 0, &bv );
00501                                    a->acl_attrval_style = ACL_STYLE_BASE;
00502 
00503                                    mr = strchr( left, '/' );
00504                                    if ( mr != NULL ) {
00505                                           mr[ 0 ] = '\0';
00506                                           mr++;
00507 
00508                                           a->acl_attrval_mr = mr_find( mr );
00509                                           if ( a->acl_attrval_mr == NULL ) {
00510                                                  Debug( LDAP_DEBUG_ANY, "%s: line %d: "
00511                                                         "invalid matching rule \"%s\".\n",
00512                                                         fname, lineno, mr );
00513                                                  goto fail;
00514                                           }
00515 
00516                                           if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) )
00517                                           {
00518                                                  char   buf[ SLAP_TEXT_BUFLEN ];
00519 
00520                                                  snprintf( buf, sizeof( buf ),
00521                                                         "matching rule \"%s\" use "
00522                                                         "with attr \"%s\" not appropriate.",
00523                                                         mr, a->acl_attrs[ 0 ].an_name.bv_val );
00524                                                         
00525 
00526                                                  Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
00527                                                         fname, lineno, buf );
00528                                                  goto fail;
00529                                           }
00530                                    }
00531                                    
00532                                    if ( style != NULL ) {
00533                                           if ( strcasecmp( style, "regex" ) == 0 ) {
00534                                                  int e = regcomp( &a->acl_attrval_re, bv.bv_val,
00535                                                         REG_EXTENDED | REG_ICASE );
00536                                                  if ( e ) {
00537                                                         char   err[SLAP_TEXT_BUFLEN],
00538                                                                buf[ SLAP_TEXT_BUFLEN ];
00539 
00540                                                         regerror( e, &a->acl_attrval_re, err, sizeof( err ) );
00541 
00542                                                         snprintf( buf, sizeof( buf ),
00543                                                                "regular expression \"%s\" bad because of %s",
00544                                                                right, err );
00545 
00546                                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
00547                                                                fname, lineno, buf );
00548                                                         goto fail;
00549                                                  }
00550                                                  a->acl_attrval_style = ACL_STYLE_REGEX;
00551 
00552                                           } else {
00553                                                  /* FIXME: if the attribute has DN syntax, we might
00554                                                   * allow one, subtree and children styles as well */
00555                                                  if ( !strcasecmp( style, "base" ) ||
00556                                                         !strcasecmp( style, "exact" ) ) {
00557                                                         a->acl_attrval_style = ACL_STYLE_BASE;
00558 
00559                                                  } else if ( a->acl_attrs[0].an_desc->ad_type->
00560                                                         sat_syntax == slap_schema.si_syn_distinguishedName )
00561                                                  {
00562                                                         if ( !strcasecmp( style, "baseObject" ) ||
00563                                                                !strcasecmp( style, "base" ) )
00564                                                         {
00565                                                                a->acl_attrval_style = ACL_STYLE_BASE;
00566                                                         } else if ( !strcasecmp( style, "onelevel" ) ||
00567                                                                !strcasecmp( style, "one" ) )
00568                                                         {
00569                                                                a->acl_attrval_style = ACL_STYLE_ONE;
00570                                                         } else if ( !strcasecmp( style, "subtree" ) ||
00571                                                                !strcasecmp( style, "sub" ) )
00572                                                         {
00573                                                                a->acl_attrval_style = ACL_STYLE_SUBTREE;
00574                                                         } else if ( !strcasecmp( style, "children" ) ) {
00575                                                                a->acl_attrval_style = ACL_STYLE_CHILDREN;
00576                                                         } else {
00577                                                                char   buf[ SLAP_TEXT_BUFLEN ];
00578 
00579                                                                snprintf( buf, sizeof( buf ),
00580                                                                       "unknown val.<style> \"%s\" for attributeType \"%s\" "
00581                                                                              "with DN syntax.",
00582                                                                       style,
00583                                                                       a->acl_attrs[0].an_desc->ad_cname.bv_val );
00584 
00585                                                                Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, 
00586                                                                       "%s: line %d: %s\n",
00587                                                                       fname, lineno, buf );
00588                                                                goto fail;
00589                                                         }
00590 
00591                                                         rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL );
00592                                                         if ( rc != LDAP_SUCCESS ) {
00593                                                                char   buf[ SLAP_TEXT_BUFLEN ];
00594 
00595                                                                snprintf( buf, sizeof( buf ),
00596                                                                       "unable to normalize DN \"%s\" "
00597                                                                       "for attributeType \"%s\" (%d).",
00598                                                                       bv.bv_val,
00599                                                                       a->acl_attrs[0].an_desc->ad_cname.bv_val,
00600                                                                       rc );
00601                                                                Debug( LDAP_DEBUG_ANY, 
00602                                                                       "%s: line %d: %s\n",
00603                                                                       fname, lineno, buf );
00604                                                                goto fail;
00605                                                         }
00606 
00607                                                  } else {
00608                                                         char   buf[ SLAP_TEXT_BUFLEN ];
00609 
00610                                                         snprintf( buf, sizeof( buf ),
00611                                                                "unknown val.<style> \"%s\" for attributeType \"%s\".",
00612                                                                style, a->acl_attrs[0].an_desc->ad_cname.bv_val );
00613                                                         Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL, 
00614                                                                "%s: line %d: %s\n",
00615                                                                fname, lineno, buf );
00616                                                         goto fail;
00617                                                  }
00618                                           }
00619                                    }
00620 
00621                                    /* Check for appropriate matching rule */
00622                                    if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
00623                                           ber_dupbv( &a->acl_attrval, &bv );
00624 
00625                                    } else if ( BER_BVISNULL( &a->acl_attrval ) ) {
00626                                           int           rc;
00627                                           const char    *text;
00628 
00629                                           if ( a->acl_attrval_mr == NULL ) {
00630                                                  a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality;
00631                                           }
00632 
00633                                           if ( a->acl_attrval_mr == NULL ) {
00634                                                  Debug( LDAP_DEBUG_ANY, "%s: line %d: "
00635                                                         "attr \"%s\" does not have an EQUALITY matching rule.\n",
00636                                                         fname, lineno, a->acl_attrs[ 0 ].an_name.bv_val );
00637                                                  goto fail;
00638                                           }
00639 
00640                                           rc = asserted_value_validate_normalize(
00641                                                  a->acl_attrs[ 0 ].an_desc,
00642                                                  a->acl_attrval_mr,
00643                                                  SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
00644                                                  &bv,
00645                                                  &a->acl_attrval,
00646                                                  &text,
00647                                                  NULL );
00648                                           if ( rc != LDAP_SUCCESS ) {
00649                                                  char   buf[ SLAP_TEXT_BUFLEN ];
00650 
00651                                                  snprintf( buf, sizeof( buf ), "%s: line %d: "
00652                                                         " attr \"%s\" normalization failed (%d: %s)",
00653                                                         fname, lineno,
00654                                                         a->acl_attrs[ 0 ].an_name.bv_val, rc, text );
00655                                                  Debug( LDAP_DEBUG_ANY, "%s: line %d: %s.\n",
00656                                                         fname, lineno, buf );
00657                                                  goto fail;
00658                                           }
00659                                    }
00660 
00661                             } else {
00662                                    Debug( LDAP_DEBUG_ANY,
00663                                           "%s: line %d: expecting <what> got \"%s\"\n",
00664                                        fname, lineno, left );
00665                                    goto fail;
00666                             }
00667                      }
00668 
00669                      if ( !BER_BVISNULL( &a->acl_dn_pat ) && 
00670                                    ber_bvccmp( &a->acl_dn_pat, '*' ) )
00671                      {
00672                             free( a->acl_dn_pat.bv_val );
00673                             BER_BVZERO( &a->acl_dn_pat );
00674                             a->acl_dn_style = ACL_STYLE_REGEX;
00675                      }
00676                      
00677                      if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
00678                                    a->acl_dn_style != ACL_STYLE_REGEX ) 
00679                      {
00680                             if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
00681                                    struct berval bv;
00682                                    rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
00683                                    if ( rc != LDAP_SUCCESS ) {
00684                                           Debug( LDAP_DEBUG_ANY,
00685                                                  "%s: line %d: bad DN \"%s\" in to DN clause\n",
00686                                                  fname, lineno, a->acl_dn_pat.bv_val );
00687                                           goto fail;
00688                                    }
00689                                    free( a->acl_dn_pat.bv_val );
00690                                    a->acl_dn_pat = bv;
00691 
00692                             } else {
00693                                    int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
00694                                           REG_EXTENDED | REG_ICASE );
00695                                    if ( e ) {
00696                                           char   err[ SLAP_TEXT_BUFLEN ],
00697                                                  buf[ SLAP_TEXT_BUFLEN ];
00698 
00699                                           regerror( e, &a->acl_dn_re, err, sizeof( err ) );
00700                                           snprintf( buf, sizeof( buf ),
00701                                                  "regular expression \"%s\" bad because of %s",
00702                                                  right, err );
00703                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
00704                                                  fname, lineno, buf );
00705                                           goto fail;
00706                                    }
00707                             }
00708                      }
00709 
00710               /* by clause - select who has what access to entries */
00711               } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
00712                      if ( a == NULL ) {
00713                             Debug( LDAP_DEBUG_ANY, "%s: line %d: "
00714                                    "to clause required before by clause in access line\n",
00715                                    fname, lineno, 0 );
00716                             goto fail;
00717                      }
00718 
00719                      /*
00720                       * by clause consists of <who> and <access>
00721                       */
00722 
00723                      if ( ++i == argc ) {
00724                             Debug( LDAP_DEBUG_ANY,
00725                                    "%s: line %d: premature EOL: expecting <who>\n",
00726                                    fname, lineno, 0 );
00727                             goto fail;
00728                      }
00729 
00730                      b = (Access *) ch_calloc( 1, sizeof(Access) );
00731 
00732                      ACL_INVALIDATE( b->a_access_mask );
00733 
00734                      /* get <who> */
00735                      for ( ; i < argc; i++ ) {
00736                             slap_style_t  sty = ACL_STYLE_REGEX;
00737                             char          *style_modifier = NULL;
00738                             char          *style_level = NULL;
00739                             int           level = 0;
00740                             int           expand = 0;
00741                             slap_dn_access       *bdn = &b->a_dn;
00742                             int           is_realdn = 0;
00743 
00744                             split( argv[i], '=', &left, &right );
00745                             split( left, '.', &left, &style );
00746                             if ( style ) {
00747                                    split( style, ',', &style, &style_modifier );
00748 
00749                                    if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
00750                                           split( style, '{', &style, &style_level );
00751                                           if ( style_level != NULL ) {
00752                                                  char *p = strchr( style_level, '}' );
00753                                                  if ( p == NULL ) {
00754                                                         Debug( LDAP_DEBUG_ANY,
00755                                                                "%s: line %d: premature eol: "
00756                                                                "expecting closing '}' in \"level{n}\"\n",
00757                                                                fname, lineno, 0 );
00758                                                         goto fail;
00759                                                  } else if ( p == style_level ) {
00760                                                         Debug( LDAP_DEBUG_ANY,
00761                                                                "%s: line %d: empty level "
00762                                                                "in \"level{n}\"\n",
00763                                                                fname, lineno, 0 );
00764                                                         goto fail;
00765                                                  }
00766                                                  p[0] = '\0';
00767                                           }
00768                                    }
00769                             }
00770 
00771                             if ( style == NULL || *style == '\0' ||
00772                                    strcasecmp( style, "exact" ) == 0 ||
00773                                    strcasecmp( style, "baseObject" ) == 0 ||
00774                                    strcasecmp( style, "base" ) == 0 )
00775                             {
00776                                    sty = ACL_STYLE_BASE;
00777 
00778                             } else if ( strcasecmp( style, "onelevel" ) == 0 ||
00779                                    strcasecmp( style, "one" ) == 0 )
00780                             {
00781                                    sty = ACL_STYLE_ONE;
00782 
00783                             } else if ( strcasecmp( style, "subtree" ) == 0 ||
00784                                    strcasecmp( style, "sub" ) == 0 )
00785                             {
00786                                    sty = ACL_STYLE_SUBTREE;
00787 
00788                             } else if ( strcasecmp( style, "children" ) == 0 ) {
00789                                    sty = ACL_STYLE_CHILDREN;
00790 
00791                             } else if ( strcasecmp( style, "level" ) == 0 )
00792                             {
00793                                    if ( lutil_atoi( &level, style_level ) != 0 ) {
00794                                           Debug( LDAP_DEBUG_ANY,
00795                                                  "%s: line %d: unable to parse level "
00796                                                  "in \"level{n}\"\n",
00797                                                  fname, lineno, 0 );
00798                                           goto fail;
00799                                    }
00800 
00801                                    sty = ACL_STYLE_LEVEL;
00802 
00803                             } else if ( strcasecmp( style, "regex" ) == 0 ) {
00804                                    sty = ACL_STYLE_REGEX;
00805 
00806                             } else if ( strcasecmp( style, "expand" ) == 0 ) {
00807                                    sty = ACL_STYLE_EXPAND;
00808 
00809                             } else if ( strcasecmp( style, "ip" ) == 0 ) {
00810                                    sty = ACL_STYLE_IP;
00811 
00812                             } else if ( strcasecmp( style, "ipv6" ) == 0 ) {
00813 #ifndef LDAP_PF_INET6
00814                                    Debug( LDAP_DEBUG_ANY,
00815                                           "%s: line %d: IPv6 not supported\n",
00816                                           fname, lineno, 0 );
00817 #endif /* ! LDAP_PF_INET6 */
00818                                    sty = ACL_STYLE_IPV6;
00819 
00820                             } else if ( strcasecmp( style, "path" ) == 0 ) {
00821                                    sty = ACL_STYLE_PATH;
00822 #ifndef LDAP_PF_LOCAL
00823                                    Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
00824                                           "%s: line %d: "
00825                                           "\"path\" style modifier is useless without local.\n",
00826                                           fname, lineno, 0 );
00827                                    goto fail;
00828 #endif /* LDAP_PF_LOCAL */
00829 
00830                             } else {
00831                                    Debug( LDAP_DEBUG_ANY,
00832                                           "%s: line %d: unknown style \"%s\" in by clause\n",
00833                                           fname, lineno, style );
00834                                    goto fail;
00835                             }
00836 
00837                             if ( style_modifier &&
00838                                    strcasecmp( style_modifier, "expand" ) == 0 )
00839                             {
00840                                    switch ( sty ) {
00841                                    case ACL_STYLE_REGEX:
00842                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
00843                                                  "\"regex\" style implies \"expand\" modifier.\n",
00844                                                  fname, lineno, 0 );
00845                                           goto fail;
00846                                           break;
00847 
00848                                    case ACL_STYLE_EXPAND:
00849                                           break;
00850 
00851                                    default:
00852                                           /* we'll see later if it's pertinent */
00853                                           expand = 1;
00854                                           break;
00855                                    }
00856                             }
00857 
00858                             if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
00859                                    is_realdn = 1;
00860                                    bdn = &b->a_realdn;
00861                                    left += STRLENOF( "real" );
00862                             }
00863 
00864                             if ( strcasecmp( left, "*" ) == 0 ) {
00865                                    if ( is_realdn ) {
00866                                           goto fail;
00867                                    }
00868 
00869                                    ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
00870                                    sty = ACL_STYLE_REGEX;
00871 
00872                             } else if ( strcasecmp( left, "anonymous" ) == 0 ) {
00873                                    ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
00874                                    sty = ACL_STYLE_ANONYMOUS;
00875 
00876                             } else if ( strcasecmp( left, "users" ) == 0 ) {
00877                                    ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
00878                                    sty = ACL_STYLE_USERS;
00879 
00880                             } else if ( strcasecmp( left, "self" ) == 0 ) {
00881                                    ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
00882                                    sty = ACL_STYLE_SELF;
00883 
00884                             } else if ( strcasecmp( left, "dn" ) == 0 ) {
00885                                    if ( sty == ACL_STYLE_REGEX ) {
00886                                           bdn->a_style = ACL_STYLE_REGEX;
00887                                           if ( right == NULL ) {
00888                                                  /* no '=' */
00889                                                  ber_str2bv("users",
00890                                                         STRLENOF( "users" ),
00891                                                         1, &bv);
00892                                                  bdn->a_style = ACL_STYLE_USERS;
00893 
00894                                           } else if (*right == '\0' ) {
00895                                                  /* dn="" */
00896                                                  ber_str2bv("anonymous",
00897                                                         STRLENOF( "anonymous" ),
00898                                                         1, &bv);
00899                                                  bdn->a_style = ACL_STYLE_ANONYMOUS;
00900 
00901                                           } else if ( strcmp( right, "*" ) == 0 ) {
00902                                                  /* dn=* */
00903                                                  /* any or users?  users for now */
00904                                                  ber_str2bv("users",
00905                                                         STRLENOF( "users" ),
00906                                                         1, &bv);
00907                                                  bdn->a_style = ACL_STYLE_USERS;
00908 
00909                                           } else if ( strcmp( right, ".+" ) == 0
00910                                                  || strcmp( right, "^.+" ) == 0
00911                                                  || strcmp( right, ".+$" ) == 0
00912                                                  || strcmp( right, "^.+$" ) == 0
00913                                                  || strcmp( right, ".+$$" ) == 0
00914                                                  || strcmp( right, "^.+$$" ) == 0 )
00915                                           {
00916                                                  ber_str2bv("users",
00917                                                         STRLENOF( "users" ),
00918                                                         1, &bv);
00919                                                  bdn->a_style = ACL_STYLE_USERS;
00920 
00921                                           } else if ( strcmp( right, ".*" ) == 0
00922                                                  || strcmp( right, "^.*" ) == 0
00923                                                  || strcmp( right, ".*$" ) == 0
00924                                                  || strcmp( right, "^.*$" ) == 0
00925                                                  || strcmp( right, ".*$$" ) == 0
00926                                                  || strcmp( right, "^.*$$" ) == 0 )
00927                                           {
00928                                                  ber_str2bv("*",
00929                                                         STRLENOF( "*" ),
00930                                                         1, &bv);
00931 
00932                                           } else {
00933                                                  acl_regex_normalized_dn( right, &bv );
00934                                                  if ( !ber_bvccmp( &bv, '*' ) ) {
00935                                                         regtest( fname, lineno, bv.bv_val );
00936                                                  }
00937                                           }
00938 
00939                                    } else if ( right == NULL || *right == '\0' ) {
00940                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
00941                                                  "missing \"=\" in (or value after) \"%s\" "
00942                                                  "in by clause\n",
00943                                                  fname, lineno, left );
00944                                           goto fail;
00945 
00946                                    } else {
00947                                           ber_str2bv( right, 0, 1, &bv );
00948                                    }
00949 
00950                             } else {
00951                                    BER_BVZERO( &bv );
00952                             }
00953 
00954                             if ( !BER_BVISNULL( &bv ) ) {
00955                                    if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
00956                                           Debug( LDAP_DEBUG_ANY,
00957                                                  "%s: line %d: dn pattern already specified.\n",
00958                                                  fname, lineno, 0 );
00959                                           goto fail;
00960                                    }
00961 
00962                                    if ( sty != ACL_STYLE_REGEX &&
00963                                                  sty != ACL_STYLE_ANONYMOUS &&
00964                                                  sty != ACL_STYLE_USERS &&
00965                                                  sty != ACL_STYLE_SELF &&
00966                                                  expand == 0 )
00967                                    {
00968                                           rc = dnNormalize(0, NULL, NULL,
00969                                                  &bv, &bdn->a_pat, NULL);
00970                                           if ( rc != LDAP_SUCCESS ) {
00971                                                  Debug( LDAP_DEBUG_ANY,
00972                                                         "%s: line %d: bad DN \"%s\" in by DN clause\n",
00973                                                         fname, lineno, bv.bv_val );
00974                                                  goto fail;
00975                                           }
00976                                           free( bv.bv_val );
00977                                           if ( sty == ACL_STYLE_BASE
00978                                                  && be != NULL
00979                                                  && !BER_BVISNULL( &be->be_rootndn )
00980                                                  && dn_match( &bdn->a_pat, &be->be_rootndn ) )
00981                                           {
00982                                                  Debug( LDAP_DEBUG_ANY,
00983                                                         "%s: line %d: rootdn is always granted "
00984                                                         "unlimited privileges.\n",
00985                                                         fname, lineno, 0 );
00986                                           }
00987 
00988                                    } else {
00989                                           bdn->a_pat = bv;
00990                                    }
00991                                    bdn->a_style = sty;
00992                                    if ( expand ) {
00993                                           char   *exp;
00994                                           int    gotit = 0;
00995 
00996                                           for ( exp = strchr( bdn->a_pat.bv_val, '$' );
00997                                                  exp && (ber_len_t)(exp - bdn->a_pat.bv_val)
00998                                                         < bdn->a_pat.bv_len;
00999                                                  exp = strchr( exp, '$' ) )
01000                                           {
01001                                                  if ( ( isdigit( (unsigned char) exp[ 1 ] ) ||
01002                                                             exp[ 1 ] == '{' ) ) {
01003                                                         gotit = 1;
01004                                                         break;
01005                                                  }
01006                                           }
01007 
01008                                           if ( gotit == 1 ) {
01009                                                  bdn->a_expand = expand;
01010 
01011                                           } else {
01012                                                  Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01013                                                         "\"expand\" used with no expansions in \"pattern\".\n",
01014                                                         fname, lineno, 0 );
01015                                                  goto fail;
01016                                           } 
01017                                    }
01018                                    if ( sty == ACL_STYLE_SELF ) {
01019                                           bdn->a_self_level = level;
01020 
01021                                    } else {
01022                                           if ( level < 0 ) {
01023                                                  Debug( LDAP_DEBUG_ANY,
01024                                                         "%s: line %d: bad negative level \"%d\" "
01025                                                         "in by DN clause\n",
01026                                                         fname, lineno, level );
01027                                                  goto fail;
01028                                           } else if ( level == 1 ) {
01029                                                  Debug( LDAP_DEBUG_ANY,
01030                                                         "%s: line %d: \"onelevel\" should be used "
01031                                                         "instead of \"level{1}\" in by DN clause\n",
01032                                                         fname, lineno, 0 );
01033                                           } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
01034                                                  Debug( LDAP_DEBUG_ANY,
01035                                                         "%s: line %d: \"base\" should be used "
01036                                                         "instead of \"level{0}\" in by DN clause\n",
01037                                                         fname, lineno, 0 );
01038                                           }
01039 
01040                                           bdn->a_level = level;
01041                                    }
01042                                    continue;
01043                             }
01044 
01045                             if ( strcasecmp( left, "dnattr" ) == 0 ) {
01046                                    if ( right == NULL || right[0] == '\0' ) {
01047                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01048                                                  "missing \"=\" in (or value after) \"%s\" "
01049                                                  "in by clause\n",
01050                                                  fname, lineno, left );
01051                                           goto fail;
01052                                    }
01053 
01054                                    if( bdn->a_at != NULL ) {
01055                                           Debug( LDAP_DEBUG_ANY,
01056                                                  "%s: line %d: dnattr already specified.\n",
01057                                                  fname, lineno, 0 );
01058                                           goto fail;
01059                                    }
01060 
01061                                    rc = slap_str2ad( right, &bdn->a_at, &text );
01062 
01063                                    if( rc != LDAP_SUCCESS ) {
01064                                           char   buf[ SLAP_TEXT_BUFLEN ];
01065 
01066                                           snprintf( buf, sizeof( buf ),
01067                                                  "dnattr \"%s\": %s",
01068                                                  right, text );
01069                                           Debug( LDAP_DEBUG_ANY,
01070                                                  "%s: line %d: %s\n",
01071                                                  fname, lineno, buf );
01072                                           goto fail;
01073                                    }
01074 
01075 
01076                                    if( !is_at_syntax( bdn->a_at->ad_type,
01077                                           SLAPD_DN_SYNTAX ) &&
01078                                           !is_at_syntax( bdn->a_at->ad_type,
01079                                           SLAPD_NAMEUID_SYNTAX ))
01080                                    {
01081                                           char   buf[ SLAP_TEXT_BUFLEN ];
01082 
01083                                           snprintf( buf, sizeof( buf ),
01084                                                  "dnattr \"%s\": "
01085                                                  "inappropriate syntax: %s\n",
01086                                                  right,
01087                                                  bdn->a_at->ad_type->sat_syntax_oid );
01088                                           Debug( LDAP_DEBUG_ANY,
01089                                                  "%s: line %d: %s\n",
01090                                                  fname, lineno, buf );
01091                                           goto fail;
01092                                    }
01093 
01094                                    if( bdn->a_at->ad_type->sat_equality == NULL ) {
01095                                           Debug( LDAP_DEBUG_ANY,
01096                                                  "%s: line %d: dnattr \"%s\": "
01097                                                  "inappropriate matching (no EQUALITY)\n",
01098                                                  fname, lineno, right );
01099                                           goto fail;
01100                                    }
01101 
01102                                    continue;
01103                             }
01104 
01105                             if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
01106                                    char *name = NULL;
01107                                    char *value = NULL;
01108                                    char *attr_name = SLAPD_GROUP_ATTR;
01109 
01110                                    switch ( sty ) {
01111                                    case ACL_STYLE_REGEX:
01112                                           /* legacy, tolerated */
01113                                           Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
01114                                                  "%s: line %d: "
01115                                                  "deprecated group style \"regex\"; "
01116                                                  "use \"expand\" instead.\n",
01117                                                  fname, lineno, 0 );
01118                                           sty = ACL_STYLE_EXPAND;
01119                                           break;
01120 
01121                                    case ACL_STYLE_BASE:
01122                                           /* legal, traditional */
01123                                    case ACL_STYLE_EXPAND:
01124                                           /* legal, substring expansion; supersedes regex */
01125                                           break;
01126 
01127                                    default:
01128                                           /* unknown */
01129                                           Debug( LDAP_DEBUG_ANY,
01130                                                  "%s: line %d: "
01131                                                  "inappropriate style \"%s\" in by clause.\n",
01132                                                  fname, lineno, style );
01133                                           goto fail;
01134                                    }
01135 
01136                                    if ( right == NULL || right[0] == '\0' ) {
01137                                           Debug( LDAP_DEBUG_ANY,
01138                                                  "%s: line %d: "
01139                                                  "missing \"=\" in (or value after) \"%s\" "
01140                                                  "in by clause.\n",
01141                                                  fname, lineno, left );
01142                                           goto fail;
01143                                    }
01144 
01145                                    if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
01146                                           Debug( LDAP_DEBUG_ANY,
01147                                                  "%s: line %d: group pattern already specified.\n",
01148                                                  fname, lineno, 0 );
01149                                           goto fail;
01150                                    }
01151 
01152                                    /* format of string is
01153                                           "group/objectClassValue/groupAttrName" */
01154                                    if ( ( value = strchr(left, '/') ) != NULL ) {
01155                                           *value++ = '\0';
01156                                           if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
01157                                                  *name++ = '\0';
01158                                           }
01159                                    }
01160 
01161                                    b->a_group_style = sty;
01162                                    if ( sty == ACL_STYLE_EXPAND ) {
01163                                           acl_regex_normalized_dn( right, &bv );
01164                                           if ( !ber_bvccmp( &bv, '*' ) ) {
01165                                                  regtest( fname, lineno, bv.bv_val );
01166                                           }
01167                                           b->a_group_pat = bv;
01168 
01169                                    } else {
01170                                           ber_str2bv( right, 0, 0, &bv );
01171                                           rc = dnNormalize( 0, NULL, NULL, &bv,
01172                                                  &b->a_group_pat, NULL );
01173                                           if ( rc != LDAP_SUCCESS ) {
01174                                                  Debug( LDAP_DEBUG_ANY,
01175                                                         "%s: line %d: bad DN \"%s\".\n",
01176                                                         fname, lineno, right );
01177                                                  goto fail;
01178                                           }
01179                                    }
01180 
01181                                    if ( value && *value ) {
01182                                           b->a_group_oc = oc_find( value );
01183                                           *--value = '/';
01184 
01185                                           if ( b->a_group_oc == NULL ) {
01186                                                  Debug( LDAP_DEBUG_ANY,
01187                                                         "%s: line %d: group objectclass "
01188                                                         "\"%s\" unknown.\n",
01189                                                         fname, lineno, value );
01190                                                  goto fail;
01191                                           }
01192 
01193                                    } else {
01194                                           b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );
01195 
01196                                           if( b->a_group_oc == NULL ) {
01197                                                  Debug( LDAP_DEBUG_ANY,
01198                                                         "%s: line %d: group default objectclass "
01199                                                         "\"%s\" unknown.\n",
01200                                                         fname, lineno, SLAPD_GROUP_CLASS );
01201                                                  goto fail;
01202                                           }
01203                                    }
01204 
01205                                    if ( is_object_subclass( slap_schema.si_oc_referral,
01206                                           b->a_group_oc ) )
01207                                    {
01208                                           Debug( LDAP_DEBUG_ANY,
01209                                                  "%s: line %d: group objectclass \"%s\" "
01210                                                  "is subclass of referral.\n",
01211                                                  fname, lineno, value );
01212                                           goto fail;
01213                                    }
01214 
01215                                    if ( is_object_subclass( slap_schema.si_oc_alias,
01216                                           b->a_group_oc ) )
01217                                    {
01218                                           Debug( LDAP_DEBUG_ANY,
01219                                                  "%s: line %d: group objectclass \"%s\" "
01220                                                  "is subclass of alias.\n",
01221                                                  fname, lineno, value );
01222                                           goto fail;
01223                                    }
01224 
01225                                    if ( name && *name ) {
01226                                           attr_name = name;
01227                                           *--name = '/';
01228 
01229                                    }
01230 
01231                                    rc = slap_str2ad( attr_name, &b->a_group_at, &text );
01232                                    if ( rc != LDAP_SUCCESS ) {
01233                                           char   buf[ SLAP_TEXT_BUFLEN ];
01234 
01235                                           snprintf( buf, sizeof( buf ),
01236                                                  "group \"%s\": %s.",
01237                                                  right, text );
01238                                           Debug( LDAP_DEBUG_ANY,
01239                                                  "%s: line %d: %s\n",
01240                                                  fname, lineno, buf );
01241                                           goto fail;
01242                                    }
01243 
01244                                    if ( !is_at_syntax( b->a_group_at->ad_type,
01245                                                  SLAPD_DN_SYNTAX ) /* e.g. "member" */
01246                                           && !is_at_syntax( b->a_group_at->ad_type,
01247                                                  SLAPD_NAMEUID_SYNTAX ) /* e.g. memberUID */
01248                                           && !is_at_subtype( b->a_group_at->ad_type,
01249                                                  slap_schema.si_ad_labeledURI->ad_type ) /* e.g. memberURL */ )
01250                                    {
01251                                           char   buf[ SLAP_TEXT_BUFLEN ];
01252 
01253                                           snprintf( buf, sizeof( buf ),
01254                                                  "group \"%s\" attr \"%s\": inappropriate syntax: %s; "
01255                                                  "must be " SLAPD_DN_SYNTAX " (DN), "
01256                                                  SLAPD_NAMEUID_SYNTAX " (NameUID) "
01257                                                  "or a subtype of labeledURI.",
01258                                                  right,
01259                                                  attr_name,
01260                                                  at_syntax( b->a_group_at->ad_type ) );
01261                                           Debug( LDAP_DEBUG_ANY,
01262                                                  "%s: line %d: %s\n",
01263                                                  fname, lineno, buf );
01264                                           goto fail;
01265                                    }
01266 
01267 
01268                                    {
01269                                           int rc;
01270                                           ObjectClass *ocs[2];
01271 
01272                                           ocs[0] = b->a_group_oc;
01273                                           ocs[1] = NULL;
01274 
01275                                           rc = oc_check_allowed( b->a_group_at->ad_type,
01276                                                  ocs, NULL );
01277 
01278                                           if( rc != 0 ) {
01279                                                  char   buf[ SLAP_TEXT_BUFLEN ];
01280 
01281                                                  snprintf( buf, sizeof( buf ),
01282                                                         "group: \"%s\" not allowed by \"%s\".",
01283                                                         b->a_group_at->ad_cname.bv_val,
01284                                                         b->a_group_oc->soc_oid );
01285                                                  Debug( LDAP_DEBUG_ANY, "%s: line %d: %s\n",
01286                                                         fname, lineno, buf );
01287                                                  goto fail;
01288                                           }
01289                                    }
01290                                    continue;
01291                             }
01292 
01293                             if ( strcasecmp( left, "peername" ) == 0 ) {
01294                                    switch ( sty ) {
01295                                    case ACL_STYLE_REGEX:
01296                                    case ACL_STYLE_BASE:
01297                                           /* legal, traditional */
01298                                    case ACL_STYLE_EXPAND:
01299                                           /* cheap replacement to regex for simple expansion */
01300                                    case ACL_STYLE_IP:
01301                                    case ACL_STYLE_IPV6:
01302                                    case ACL_STYLE_PATH:
01303                                           /* legal, peername specific */
01304                                           break;
01305 
01306                                    default:
01307                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01308                                                  "inappropriate style \"%s\" in by clause.\n",
01309                                               fname, lineno, style );
01310                                           goto fail;
01311                                    }
01312 
01313                                    if ( right == NULL || right[0] == '\0' ) {
01314                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01315                                                  "missing \"=\" in (or value after) \"%s\" "
01316                                                  "in by clause.\n",
01317                                                  fname, lineno, left );
01318                                           goto fail;
01319                                    }
01320 
01321                                    if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
01322                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01323                                                  "peername pattern already specified.\n",
01324                                                  fname, lineno, 0 );
01325                                           goto fail;
01326                                    }
01327 
01328                                    b->a_peername_style = sty;
01329                                    if ( sty == ACL_STYLE_REGEX ) {
01330                                           acl_regex_normalized_dn( right, &bv );
01331                                           if ( !ber_bvccmp( &bv, '*' ) ) {
01332                                                  regtest( fname, lineno, bv.bv_val );
01333                                           }
01334                                           b->a_peername_pat = bv;
01335 
01336                                    } else {
01337                                           ber_str2bv( right, 0, 1, &b->a_peername_pat );
01338 
01339                                           if ( sty == ACL_STYLE_IP ) {
01340                                                  char          *addr = NULL,
01341                                                                *mask = NULL,
01342                                                                *port = NULL;
01343 
01344                                                  split( right, '{', &addr, &port );
01345                                                  split( addr, '%', &addr, &mask );
01346 
01347                                                  b->a_peername_addr = inet_addr( addr );
01348                                                  if ( b->a_peername_addr == (unsigned long)(-1) ) {
01349                                                         /* illegal address */
01350                                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01351                                                                "illegal peername address \"%s\".\n",
01352                                                                fname, lineno, addr );
01353                                                         goto fail;
01354                                                  }
01355 
01356                                                  b->a_peername_mask = (unsigned long)(-1);
01357                                                  if ( mask != NULL ) {
01358                                                         b->a_peername_mask = inet_addr( mask );
01359                                                         if ( b->a_peername_mask ==
01360                                                                (unsigned long)(-1) )
01361                                                         {
01362                                                                /* illegal mask */
01363                                                                Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01364                                                                       "illegal peername address mask "
01365                                                                       "\"%s\".\n",
01366                                                                       fname, lineno, mask );
01367                                                                goto fail;
01368                                                         }
01369                                                  } 
01370 
01371                                                  b->a_peername_port = -1;
01372                                                  if ( port ) {
01373                                                         char   *end = NULL;
01374 
01375                                                         b->a_peername_port = strtol( port, &end, 10 );
01376                                                         if ( end == port || end[0] != '}' ) {
01377                                                                /* illegal port */
01378                                                                Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01379                                                                       "illegal peername port specification "
01380                                                                       "\"{%s}\".\n",
01381                                                                       fname, lineno, port );
01382                                                                goto fail;
01383                                                         }
01384                                                  }
01385 
01386 #ifdef LDAP_PF_INET6
01387                                           } else if ( sty == ACL_STYLE_IPV6 ) {
01388                                                  char          *addr = NULL,
01389                                                                *mask = NULL,
01390                                                                *port = NULL;
01391 
01392                                                  split( right, '{', &addr, &port );
01393                                                  split( addr, '%', &addr, &mask );
01394 
01395                                                  if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) {
01396                                                         /* illegal address */
01397                                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01398                                                                "illegal peername address \"%s\".\n",
01399                                                                fname, lineno, addr );
01400                                                         goto fail;
01401                                                  }
01402 
01403                                                  if ( mask == NULL ) {
01404                                                         mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
01405                                                  }
01406 
01407                                                  if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) {
01408                                                         /* illegal mask */
01409                                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01410                                                                "illegal peername address mask "
01411                                                                "\"%s\".\n",
01412                                                                fname, lineno, mask );
01413                                                         goto fail;
01414                                                  }
01415 
01416                                                  b->a_peername_port = -1;
01417                                                  if ( port ) {
01418                                                         char   *end = NULL;
01419 
01420                                                         b->a_peername_port = strtol( port, &end, 10 );
01421                                                         if ( end == port || end[0] != '}' ) {
01422                                                                /* illegal port */
01423                                                                Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01424                                                                       "illegal peername port specification "
01425                                                                       "\"{%s}\".\n",
01426                                                                       fname, lineno, port );
01427                                                                goto fail;
01428                                                         }
01429                                                  }
01430 #endif /* LDAP_PF_INET6 */
01431                                           }
01432                                    }
01433                                    continue;
01434                             }
01435 
01436                             if ( strcasecmp( left, "sockname" ) == 0 ) {
01437                                    switch ( sty ) {
01438                                    case ACL_STYLE_REGEX:
01439                                    case ACL_STYLE_BASE:
01440                                           /* legal, traditional */
01441                                    case ACL_STYLE_EXPAND:
01442                                           /* cheap replacement to regex for simple expansion */
01443                                           break;
01444 
01445                                    default:
01446                                           /* unknown */
01447                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01448                                                  "inappropriate style \"%s\" in by clause\n",
01449                                               fname, lineno, style );
01450                                           goto fail;
01451                                    }
01452 
01453                                    if ( right == NULL || right[0] == '\0' ) {
01454                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01455                                                  "missing \"=\" in (or value after) \"%s\" "
01456                                                  "in by clause\n",
01457                                                  fname, lineno, left );
01458                                           goto fail;
01459                                    }
01460 
01461                                    if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
01462                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01463                                                  "sockname pattern already specified.\n",
01464                                                  fname, lineno, 0 );
01465                                           goto fail;
01466                                    }
01467 
01468                                    b->a_sockname_style = sty;
01469                                    if ( sty == ACL_STYLE_REGEX ) {
01470                                           acl_regex_normalized_dn( right, &bv );
01471                                           if ( !ber_bvccmp( &bv, '*' ) ) {
01472                                                  regtest( fname, lineno, bv.bv_val );
01473                                           }
01474                                           b->a_sockname_pat = bv;
01475                                           
01476                                    } else {
01477                                           ber_str2bv( right, 0, 1, &b->a_sockname_pat );
01478                                    }
01479                                    continue;
01480                             }
01481 
01482                             if ( strcasecmp( left, "domain" ) == 0 ) {
01483                                    switch ( sty ) {
01484                                    case ACL_STYLE_REGEX:
01485                                    case ACL_STYLE_BASE:
01486                                    case ACL_STYLE_SUBTREE:
01487                                           /* legal, traditional */
01488                                           break;
01489 
01490                                    case ACL_STYLE_EXPAND:
01491                                           /* tolerated: means exact,expand */
01492                                           if ( expand ) {
01493                                                  Debug( LDAP_DEBUG_ANY,
01494                                                         "%s: line %d: "
01495                                                         "\"expand\" modifier "
01496                                                         "with \"expand\" style.\n",
01497                                                         fname, lineno, 0 );
01498                                           }
01499                                           sty = ACL_STYLE_BASE;
01500                                           expand = 1;
01501                                           break;
01502 
01503                                    default:
01504                                           /* unknown */
01505                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01506                                                  "inappropriate style \"%s\" in by clause.\n",
01507                                               fname, lineno, style );
01508                                           goto fail;
01509                                    }
01510 
01511                                    if ( right == NULL || right[0] == '\0' ) {
01512                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01513                                                  "missing \"=\" in (or value after) \"%s\" "
01514                                                  "in by clause.\n",
01515                                                  fname, lineno, left );
01516                                           goto fail;
01517                                    }
01518 
01519                                    if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
01520                                           Debug( LDAP_DEBUG_ANY,
01521                                                  "%s: line %d: domain pattern already specified.\n",
01522                                                  fname, lineno, 0 );
01523                                           goto fail;
01524                                    }
01525 
01526                                    b->a_domain_style = sty;
01527                                    b->a_domain_expand = expand;
01528                                    if ( sty == ACL_STYLE_REGEX ) {
01529                                           acl_regex_normalized_dn( right, &bv );
01530                                           if ( !ber_bvccmp( &bv, '*' ) ) {
01531                                                  regtest( fname, lineno, bv.bv_val );
01532                                           }
01533                                           b->a_domain_pat = bv;
01534 
01535                                    } else {
01536                                           ber_str2bv( right, 0, 1, &b->a_domain_pat );
01537                                    }
01538                                    continue;
01539                             }
01540 
01541                             if ( strcasecmp( left, "sockurl" ) == 0 ) {
01542                                    switch ( sty ) {
01543                                    case ACL_STYLE_REGEX:
01544                                    case ACL_STYLE_BASE:
01545                                           /* legal, traditional */
01546                                    case ACL_STYLE_EXPAND:
01547                                           /* cheap replacement to regex for simple expansion */
01548                                           break;
01549 
01550                                    default:
01551                                           /* unknown */
01552                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01553                                                  "inappropriate style \"%s\" in by clause.\n",
01554                                               fname, lineno, style );
01555                                           goto fail;
01556                                    }
01557 
01558                                    if ( right == NULL || right[0] == '\0' ) {
01559                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01560                                                  "missing \"=\" in (or value after) \"%s\" "
01561                                                  "in by clause.\n",
01562                                                  fname, lineno, left );
01563                                           goto fail;
01564                                    }
01565 
01566                                    if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
01567                                           Debug( LDAP_DEBUG_ANY,
01568                                                  "%s: line %d: sockurl pattern already specified.\n",
01569                                                  fname, lineno, 0 );
01570                                           goto fail;
01571                                    }
01572 
01573                                    b->a_sockurl_style = sty;
01574                                    if ( sty == ACL_STYLE_REGEX ) {
01575                                           acl_regex_normalized_dn( right, &bv );
01576                                           if ( !ber_bvccmp( &bv, '*' ) ) {
01577                                                  regtest( fname, lineno, bv.bv_val );
01578                                           }
01579                                           b->a_sockurl_pat = bv;
01580                                           
01581                                    } else {
01582                                           ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
01583                                    }
01584                                    continue;
01585                             }
01586 
01587                             if ( strcasecmp( left, "set" ) == 0 ) {
01588                                    switch ( sty ) {
01589                                           /* deprecated */
01590                                    case ACL_STYLE_REGEX:
01591                                           Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
01592                                                  "%s: line %d: "
01593                                                  "deprecated set style "
01594                                                  "\"regex\" in <by> clause; "
01595                                                  "use \"expand\" instead.\n",
01596                                                  fname, lineno, 0 );
01597                                           sty = ACL_STYLE_EXPAND;
01598                                           /* FALLTHRU */
01599                                           
01600                                    case ACL_STYLE_BASE:
01601                                    case ACL_STYLE_EXPAND:
01602                                           break;
01603 
01604                                    default:
01605                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01606                                                  "inappropriate style \"%s\" in by clause.\n",
01607                                                  fname, lineno, style );
01608                                           goto fail;
01609                                    }
01610 
01611                                    if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
01612                                           Debug( LDAP_DEBUG_ANY,
01613                                                  "%s: line %d: set attribute already specified.\n",
01614                                                  fname, lineno, 0 );
01615                                           goto fail;
01616                                    }
01617 
01618                                    if ( right == NULL || *right == '\0' ) {
01619                                           Debug( LDAP_DEBUG_ANY,
01620                                                  "%s: line %d: no set is defined.\n",
01621                                                  fname, lineno, 0 );
01622                                           goto fail;
01623                                    }
01624 
01625                                    b->a_set_style = sty;
01626                                    ber_str2bv( right, 0, 1, &b->a_set_pat );
01627 
01628                                    continue;
01629                             }
01630 
01631 #ifdef SLAP_DYNACL
01632                             {
01633                                    char          *name = NULL,
01634                                                  *opts = NULL;
01635 
01636 #if 1 /* tolerate legacy "aci" <who> */
01637                                    if ( strcasecmp( left, "aci" ) == 0 ) {
01638                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01639                                                  "undocumented deprecated \"aci\" directive "
01640                                                  "is superseded by \"dynacl/aci\".\n",
01641                                                  fname, lineno, 0 );
01642                                           name = "aci";
01643                                           
01644                                    } else
01645 #endif /* tolerate legacy "aci" <who> */
01646                                    if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
01647                                           name = &left[ STRLENOF( "dynacl/" ) ];
01648                                           opts = strchr( name, '/' );
01649                                           if ( opts ) {
01650                                                  opts[ 0 ] = '\0';
01651                                                  opts++;
01652                                           }
01653                                    }
01654 
01655                                    if ( name ) {
01656                                           if ( slap_dynacl_config( fname, lineno, b, name, opts, sty, right ) ) {
01657                                                  Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01658                                                         "unable to configure dynacl \"%s\".\n",
01659                                                         fname, lineno, name );
01660                                                  goto fail;
01661                                           }
01662 
01663                                           continue;
01664                                    }
01665                             }
01666 #endif /* SLAP_DYNACL */
01667 
01668                             if ( strcasecmp( left, "ssf" ) == 0 ) {
01669                                    if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
01670                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01671                                                  "inappropriate style \"%s\" in by clause.\n",
01672                                               fname, lineno, style );
01673                                           goto fail;
01674                                    }
01675 
01676                                    if ( b->a_authz.sai_ssf ) {
01677                                           Debug( LDAP_DEBUG_ANY,
01678                                                  "%s: line %d: ssf attribute already specified.\n",
01679                                                  fname, lineno, 0 );
01680                                           goto fail;
01681                                    }
01682 
01683                                    if ( right == NULL || *right == '\0' ) {
01684                                           Debug( LDAP_DEBUG_ANY,
01685                                                  "%s: line %d: no ssf is defined.\n",
01686                                                  fname, lineno, 0 );
01687                                           goto fail;
01688                                    }
01689 
01690                                    if ( lutil_atou( &b->a_authz.sai_ssf, right ) != 0 ) {
01691                                           Debug( LDAP_DEBUG_ANY,
01692                                                  "%s: line %d: unable to parse ssf value (%s).\n",
01693                                                  fname, lineno, right );
01694                                           goto fail;
01695                                    }
01696 
01697                                    if ( !b->a_authz.sai_ssf ) {
01698                                           Debug( LDAP_DEBUG_ANY,
01699                                                  "%s: line %d: invalid ssf value (%s).\n",
01700                                                  fname, lineno, right );
01701                                           goto fail;
01702                                    }
01703                                    continue;
01704                             }
01705 
01706                             if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
01707                                    if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
01708                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01709                                                  "inappropriate style \"%s\" in by clause.\n",
01710                                                  fname, lineno, style );
01711                                           goto fail;
01712                                    }
01713 
01714                                    if ( b->a_authz.sai_transport_ssf ) {
01715                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01716                                                  "transport_ssf attribute already specified.\n",
01717                                                  fname, lineno, 0 );
01718                                           goto fail;
01719                                    }
01720 
01721                                    if ( right == NULL || *right == '\0' ) {
01722                                           Debug( LDAP_DEBUG_ANY,
01723                                                  "%s: line %d: no transport_ssf is defined.\n",
01724                                                  fname, lineno, 0 );
01725                                           goto fail;
01726                                    }
01727 
01728                                    if ( lutil_atou( &b->a_authz.sai_transport_ssf, right ) != 0 ) {
01729                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01730                                                  "unable to parse transport_ssf value (%s).\n",
01731                                                  fname, lineno, right );
01732                                           goto fail;
01733                                    }
01734 
01735                                    if ( !b->a_authz.sai_transport_ssf ) {
01736                                           Debug( LDAP_DEBUG_ANY,
01737                                                  "%s: line %d: invalid transport_ssf value (%s).\n",
01738                                                  fname, lineno, right );
01739                                           goto fail;
01740                                    }
01741                                    continue;
01742                             }
01743 
01744                             if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
01745                                    if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
01746                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01747                                                  "inappropriate style \"%s\" in by clause.\n",
01748                                                  fname, lineno, style );
01749                                           goto fail;
01750                                    }
01751 
01752                                    if ( b->a_authz.sai_tls_ssf ) {
01753                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01754                                                  "tls_ssf attribute already specified.\n",
01755                                                  fname, lineno, 0 );
01756                                           goto fail;
01757                                    }
01758 
01759                                    if ( right == NULL || *right == '\0' ) {
01760                                           Debug( LDAP_DEBUG_ANY,
01761                                                  "%s: line %d: no tls_ssf is defined\n",
01762                                                  fname, lineno, 0 );
01763                                           goto fail;
01764                                    }
01765 
01766                                    if ( lutil_atou( &b->a_authz.sai_tls_ssf, right ) != 0 ) {
01767                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01768                                                  "unable to parse tls_ssf value (%s).\n",
01769                                                  fname, lineno, right );
01770                                           goto fail;
01771                                    }
01772 
01773                                    if ( !b->a_authz.sai_tls_ssf ) {
01774                                           Debug( LDAP_DEBUG_ANY,
01775                                                  "%s: line %d: invalid tls_ssf value (%s).\n",
01776                                                  fname, lineno, right );
01777                                           goto fail;
01778                                    }
01779                                    continue;
01780                             }
01781 
01782                             if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
01783                                    if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
01784                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01785                                                  "inappropriate style \"%s\" in by clause.\n",
01786                                                  fname, lineno, style );
01787                                           goto fail;
01788                                    }
01789 
01790                                    if ( b->a_authz.sai_sasl_ssf ) {
01791                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01792                                                  "sasl_ssf attribute already specified.\n",
01793                                                  fname, lineno, 0 );
01794                                           goto fail;
01795                                    }
01796 
01797                                    if ( right == NULL || *right == '\0' ) {
01798                                           Debug( LDAP_DEBUG_ANY,
01799                                                  "%s: line %d: no sasl_ssf is defined.\n",
01800                                                  fname, lineno, 0 );
01801                                           goto fail;
01802                                    }
01803 
01804                                    if ( lutil_atou( &b->a_authz.sai_sasl_ssf, right ) != 0 ) {
01805                                           Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01806                                                  "unable to parse sasl_ssf value (%s).\n",
01807                                                  fname, lineno, right );
01808                                           goto fail;
01809                                    }
01810 
01811                                    if ( !b->a_authz.sai_sasl_ssf ) {
01812                                           Debug( LDAP_DEBUG_ANY,
01813                                                  "%s: line %d: invalid sasl_ssf value (%s).\n",
01814                                                  fname, lineno, right );
01815                                           goto fail;
01816                                    }
01817                                    continue;
01818                             }
01819 
01820                             if ( right != NULL ) {
01821                                    /* unsplit */
01822                                    right[-1] = '=';
01823                             }
01824                             break;
01825                      }
01826 
01827                      if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) { 
01828                             /* out of arguments or plain stop */
01829 
01830                             ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
01831                             ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
01832                             b->a_type = ACL_STOP;
01833 
01834                             access_append( &a->acl_access, b );
01835                             continue;
01836                      }
01837 
01838                      if ( strcasecmp( left, "continue" ) == 0 ) {
01839                             /* plain continue */
01840 
01841                             ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
01842                             ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
01843                             b->a_type = ACL_CONTINUE;
01844 
01845                             access_append( &a->acl_access, b );
01846                             continue;
01847                      }
01848 
01849                      if ( strcasecmp( left, "break" ) == 0 ) {
01850                             /* plain continue */
01851 
01852                             ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
01853                             ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
01854                             b->a_type = ACL_BREAK;
01855 
01856                             access_append( &a->acl_access, b );
01857                             continue;
01858                      }
01859 
01860                      if ( strcasecmp( left, "by" ) == 0 ) {
01861                             /* we've gone too far */
01862                             --i;
01863                             ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
01864                             ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
01865                             b->a_type = ACL_STOP;
01866 
01867                             access_append( &a->acl_access, b );
01868                             continue;
01869                      }
01870 
01871                      /* get <access> */
01872                      {
01873                             char   *lleft = left;
01874 
01875                             if ( strncasecmp( left, "self", STRLENOF( "self" ) ) == 0 ) {
01876                                    b->a_dn_self = 1;
01877                                    lleft = &left[ STRLENOF( "self" ) ];
01878 
01879                             } else if ( strncasecmp( left, "realself", STRLENOF( "realself" ) ) == 0 ) {
01880                                    b->a_realdn_self = 1;
01881                                    lleft = &left[ STRLENOF( "realself" ) ];
01882                             }
01883 
01884                             ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( lleft ) );
01885                      }
01886 
01887                      if ( ACL_IS_INVALID( b->a_access_mask ) ) {
01888                             Debug( LDAP_DEBUG_ANY,
01889                                    "%s: line %d: expecting <access> got \"%s\".\n",
01890                                    fname, lineno, left );
01891                             goto fail;
01892                      }
01893 
01894                      b->a_type = ACL_STOP;
01895 
01896                      if ( ++i == argc ) {
01897                             /* out of arguments or plain stop */
01898                             access_append( &a->acl_access, b );
01899                             continue;
01900                      }
01901 
01902                      if ( strcasecmp( argv[i], "continue" ) == 0 ) {
01903                             /* plain continue */
01904                             b->a_type = ACL_CONTINUE;
01905 
01906                      } else if ( strcasecmp( argv[i], "break" ) == 0 ) {
01907                             /* plain continue */
01908                             b->a_type = ACL_BREAK;
01909 
01910                      } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
01911                             /* gone to far */
01912                             i--;
01913                      }
01914 
01915                      access_append( &a->acl_access, b );
01916                      b = NULL;
01917 
01918               } else {
01919                      Debug( LDAP_DEBUG_ANY,
01920                             "%s: line %d: expecting \"to\" "
01921                             "or \"by\" got \"%s\"\n",
01922                             fname, lineno, argv[i] );
01923                      goto fail;
01924               }
01925        }
01926 
01927        /* if we have no real access clause, complain and do nothing */
01928        if ( a == NULL ) {
01929               Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01930                      "warning: no access clause(s) specified in access line.\n",
01931                      fname, lineno, 0 );
01932               goto fail;
01933 
01934        } else {
01935 #ifdef LDAP_DEBUG
01936               if ( slap_debug & LDAP_DEBUG_ACL ) {
01937                      print_acl( be, a );
01938               }
01939 #endif
01940        
01941               if ( a->acl_access == NULL ) {
01942                      Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01943                             "warning: no by clause(s) specified in access line.\n",
01944                             fname, lineno, 0 );
01945                      goto fail;
01946               }
01947 
01948               if ( be != NULL ) {
01949                      if ( be->be_nsuffix == NULL ) {
01950                             Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
01951                                    "scope checking needs suffix before ACLs.\n",
01952                                    fname, lineno, 0 );
01953                             /* go ahead, since checking is not authoritative */
01954                      } else if ( !BER_BVISNULL( &be->be_nsuffix[ 1 ] ) ) {
01955                             Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
01956                                    "scope checking only applies to single-valued "
01957                                    "suffix databases\n",
01958                                    fname, lineno, 0 );
01959                             /* go ahead, since checking is not authoritative */
01960                      } else {
01961                             switch ( check_scope( be, a ) ) {
01962                             case ACL_SCOPE_UNKNOWN:
01963                                    Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
01964                                           "cannot assess the validity of the ACL scope within "
01965                                           "backend naming context\n",
01966                                           fname, lineno, 0 );
01967                                    break;
01968 
01969                             case ACL_SCOPE_WARN:
01970                                    Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
01971                                           "ACL could be out of scope within backend naming context\n",
01972                                           fname, lineno, 0 );
01973                                    break;
01974 
01975                             case ACL_SCOPE_PARTIAL:
01976                                    Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
01977                                           "ACL appears to be partially out of scope within "
01978                                           "backend naming context\n",
01979                                           fname, lineno, 0 );
01980                                    break;
01981        
01982                             case ACL_SCOPE_ERR:
01983                                    Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
01984                                           "ACL appears to be out of scope within "
01985                                           "backend naming context\n",
01986                                           fname, lineno, 0 );
01987                                    break;
01988 
01989                             default:
01990                                    break;
01991                             }
01992                      }
01993                      acl_append( &be->be_acl, a, pos );
01994 
01995               } else {
01996                      acl_append( &frontendDB->be_acl, a, pos );
01997               }
01998        }
01999 
02000        return 0;
02001 
02002 fail:
02003        if ( b ) access_free( b );
02004        if ( a ) acl_free( a );
02005        return acl_usage();
02006 }
02007 
02008 char *
02009 accessmask2str( slap_mask_t mask, char *buf, int debug )
02010 {
02011        int    none = 1;
02012        char   *ptr = buf;
02013 
02014        assert( buf != NULL );
02015 
02016        if ( ACL_IS_INVALID( mask ) ) {
02017               return "invalid";
02018        }
02019 
02020        buf[0] = '\0';
02021 
02022        if ( ACL_IS_LEVEL( mask ) ) {
02023               if ( ACL_LVL_IS_NONE(mask) ) {
02024                      ptr = lutil_strcopy( ptr, "none" );
02025 
02026               } else if ( ACL_LVL_IS_DISCLOSE(mask) ) {
02027                      ptr = lutil_strcopy( ptr, "disclose" );
02028 
02029               } else if ( ACL_LVL_IS_AUTH(mask) ) {
02030                      ptr = lutil_strcopy( ptr, "auth" );
02031 
02032               } else if ( ACL_LVL_IS_COMPARE(mask) ) {
02033                      ptr = lutil_strcopy( ptr, "compare" );
02034 
02035               } else if ( ACL_LVL_IS_SEARCH(mask) ) {
02036                      ptr = lutil_strcopy( ptr, "search" );
02037 
02038               } else if ( ACL_LVL_IS_READ(mask) ) {
02039                      ptr = lutil_strcopy( ptr, "read" );
02040 
02041               } else if ( ACL_LVL_IS_WRITE(mask) ) {
02042                      ptr = lutil_strcopy( ptr, "write" );
02043 
02044               } else if ( ACL_LVL_IS_WADD(mask) ) {
02045                      ptr = lutil_strcopy( ptr, "add" );
02046 
02047               } else if ( ACL_LVL_IS_WDEL(mask) ) {
02048                      ptr = lutil_strcopy( ptr, "delete" );
02049 
02050               } else if ( ACL_LVL_IS_MANAGE(mask) ) {
02051                      ptr = lutil_strcopy( ptr, "manage" );
02052 
02053               } else {
02054                      ptr = lutil_strcopy( ptr, "unknown" );
02055               }
02056               
02057               if ( !debug ) {
02058                      *ptr = '\0';
02059                      return buf;
02060               }
02061               *ptr++ = '(';
02062        }
02063 
02064        if( ACL_IS_ADDITIVE( mask ) ) {
02065               *ptr++ = '+';
02066 
02067        } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
02068               *ptr++ = '-';
02069 
02070        } else {
02071               *ptr++ = '=';
02072        }
02073 
02074        if ( ACL_PRIV_ISSET(mask, ACL_PRIV_MANAGE) ) {
02075               none = 0;
02076               *ptr++ = 'm';
02077        } 
02078 
02079        if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
02080               none = 0;
02081               *ptr++ = 'w';
02082 
02083        } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WADD) ) {
02084               none = 0;
02085               *ptr++ = 'a';
02086 
02087        } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) {
02088               none = 0;
02089               *ptr++ = 'z';
02090        } 
02091 
02092        if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
02093               none = 0;
02094               *ptr++ = 'r';
02095        } 
02096 
02097        if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
02098               none = 0;
02099               *ptr++ = 's';
02100        } 
02101 
02102        if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
02103               none = 0;
02104               *ptr++ = 'c';
02105        } 
02106 
02107        if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
02108               none = 0;
02109               *ptr++ = 'x';
02110        } 
02111 
02112        if ( ACL_PRIV_ISSET(mask, ACL_PRIV_DISCLOSE) ) {
02113               none = 0;
02114               *ptr++ = 'd';
02115        } 
02116 
02117        if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
02118               none = 0;
02119               *ptr++ = '0';
02120        } 
02121 
02122        if ( none ) {
02123               ptr = buf;
02124        }
02125 
02126        if ( ACL_IS_LEVEL( mask ) ) {
02127               *ptr++ = ')';
02128        }
02129 
02130        *ptr = '\0';
02131 
02132        return buf;
02133 }
02134 
02135 slap_mask_t
02136 str2accessmask( const char *str )
02137 {
02138        slap_mask_t   mask;
02139 
02140        if( !ASCII_ALPHA(str[0]) ) {
02141               int i;
02142 
02143               if ( str[0] == '=' ) {
02144                      ACL_INIT(mask);
02145 
02146               } else if( str[0] == '+' ) {
02147                      ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
02148 
02149               } else if( str[0] == '-' ) {
02150                      ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
02151 
02152               } else {
02153                      ACL_INVALIDATE(mask);
02154                      return mask;
02155               }
02156 
02157               for( i=1; str[i] != '\0'; i++ ) {
02158                      if( TOLOWER((unsigned char) str[i]) == 'm' ) {
02159                             ACL_PRIV_SET(mask, ACL_PRIV_MANAGE);
02160 
02161                      } else if( TOLOWER((unsigned char) str[i]) == 'w' ) {
02162                             ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
02163 
02164                      } else if( TOLOWER((unsigned char) str[i]) == 'a' ) {
02165                             ACL_PRIV_SET(mask, ACL_PRIV_WADD);
02166 
02167                      } else if( TOLOWER((unsigned char) str[i]) == 'z' ) {
02168                             ACL_PRIV_SET(mask, ACL_PRIV_WDEL);
02169 
02170                      } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
02171                             ACL_PRIV_SET(mask, ACL_PRIV_READ);
02172 
02173                      } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
02174                             ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
02175 
02176                      } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
02177                             ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
02178 
02179                      } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
02180                             ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
02181 
02182                      } else if( TOLOWER((unsigned char) str[i]) == 'd' ) {
02183                             ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
02184 
02185                      } else if( str[i] == '0' ) {
02186                             ACL_PRIV_SET(mask, ACL_PRIV_NONE);
02187 
02188                      } else {
02189                             ACL_INVALIDATE(mask);
02190                             return mask;
02191                      }
02192               }
02193 
02194               return mask;
02195        }
02196 
02197        if ( strcasecmp( str, "none" ) == 0 ) {
02198               ACL_LVL_ASSIGN_NONE(mask);
02199 
02200        } else if ( strcasecmp( str, "disclose" ) == 0 ) {
02201               ACL_LVL_ASSIGN_DISCLOSE(mask);
02202 
02203        } else if ( strcasecmp( str, "auth" ) == 0 ) {
02204               ACL_LVL_ASSIGN_AUTH(mask);
02205 
02206        } else if ( strcasecmp( str, "compare" ) == 0 ) {
02207               ACL_LVL_ASSIGN_COMPARE(mask);
02208 
02209        } else if ( strcasecmp( str, "search" ) == 0 ) {
02210               ACL_LVL_ASSIGN_SEARCH(mask);
02211 
02212        } else if ( strcasecmp( str, "read" ) == 0 ) {
02213               ACL_LVL_ASSIGN_READ(mask);
02214 
02215        } else if ( strcasecmp( str, "add" ) == 0 ) {
02216               ACL_LVL_ASSIGN_WADD(mask);
02217 
02218        } else if ( strcasecmp( str, "delete" ) == 0 ) {
02219               ACL_LVL_ASSIGN_WDEL(mask);
02220 
02221        } else if ( strcasecmp( str, "write" ) == 0 ) {
02222               ACL_LVL_ASSIGN_WRITE(mask);
02223 
02224        } else if ( strcasecmp( str, "manage" ) == 0 ) {
02225               ACL_LVL_ASSIGN_MANAGE(mask);
02226 
02227        } else {
02228               ACL_INVALIDATE( mask );
02229        }
02230 
02231        return mask;
02232 }
02233 
02234 static int
02235 acl_usage( void )
02236 {
02237        char *access =
02238               "<access clause> ::= access to <what> "
02239                             "[ by <who> [ <access> ] [ <control> ] ]+ \n";
02240        char *what =
02241               "<what> ::= * | dn[.<dnstyle>=<DN>] [filter=<filter>] [attrs=<attrspec>]\n"
02242               "<attrspec> ::= <attrname> [val[/<matchingRule>][.<attrstyle>]=<value>] | <attrlist>\n"
02243               "<attrlist> ::= <attr> [ , <attrlist> ]\n"
02244               "<attr> ::= <attrname> | @<objectClass> | !<objectClass> | entry | children\n";
02245 
02246        char *who =
02247               "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
02248                      "\t[ realanonymous | realusers | realself | realdn[.<dnstyle>]=<DN> ]\n"
02249                      "\t[dnattr=<attrname>]\n"
02250                      "\t[realdnattr=<attrname>]\n"
02251                      "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
02252                      "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n"
02253                      "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
02254 #ifdef SLAP_DYNACL
02255                      "\t[dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]]\n"
02256 #endif /* SLAP_DYNACL */
02257                      "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
02258               "<style> ::= exact | regex | base(Object)\n"
02259               "<dnstyle> ::= base(Object) | one(level) | sub(tree) | children | "
02260                      "exact | regex\n"
02261               "<attrstyle> ::= exact | regex | base(Object) | one(level) | "
02262                      "sub(tree) | children\n"
02263               "<peernamestyle> ::= exact | regex | ip | ipv6 | path\n"
02264               "<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n"
02265               "<access> ::= [[real]self]{<level>|<priv>}\n"
02266               "<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n"
02267               "<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n"
02268               "<control> ::= [ stop | continue | break ]\n"
02269 #ifdef SLAP_DYNACL
02270 #ifdef SLAPD_ACI_ENABLED
02271               "dynacl:\n"
02272               "\t<name>=ACI\t<pattern>=<attrname>\n"
02273 #endif /* SLAPD_ACI_ENABLED */
02274 #endif /* ! SLAP_DYNACL */
02275               "";
02276 
02277        Debug( LDAP_DEBUG_ANY, "%s%s%s\n", access, what, who );
02278 
02279        return 1;
02280 }
02281 
02282 /*
02283  * Set pattern to a "normalized" DN from src.
02284  * At present it simply eats the (optional) space after 
02285  * a RDN separator (,)
02286  * Eventually will evolve in a more complete normalization
02287  */
02288 static void
02289 acl_regex_normalized_dn(
02290        const char *src,
02291        struct berval *pattern )
02292 {
02293        char *str, *p;
02294        ber_len_t len;
02295 
02296        str = ch_strdup( src );
02297        len = strlen( src );
02298 
02299        for ( p = str; p && p[0]; p++ ) {
02300               /* escape */
02301               if ( p[0] == '\\' && p[1] ) {
02302                      /* 
02303                       * if escaping a hex pair we should
02304                       * increment p twice; however, in that 
02305                       * case the second hex number does 
02306                       * no harm
02307                       */
02308                      p++;
02309               }
02310 
02311               if ( p[0] == ',' && p[1] == ' ' ) {
02312                      char *q;
02313                      
02314                      /*
02315                       * too much space should be an error if we are pedantic
02316                       */
02317                      for ( q = &p[2]; q[0] == ' '; q++ ) {
02318                             /* DO NOTHING */ ;
02319                      }
02320                      AC_MEMCPY( p+1, q, len-(q-str)+1);
02321               }
02322        }
02323        pattern->bv_val = str;
02324        pattern->bv_len = p - str;
02325 
02326        return;
02327 }
02328 
02329 static void
02330 split(
02331     char      *line,
02332     int              splitchar,
02333     char      **left,
02334     char      **right )
02335 {
02336        *left = line;
02337        if ( (*right = strchr( line, splitchar )) != NULL ) {
02338               *((*right)++) = '\0';
02339        }
02340 }
02341 
02342 static void
02343 access_append( Access **l, Access *a )
02344 {
02345        for ( ; *l != NULL; l = &(*l)->a_next ) {
02346               ;      /* Empty */
02347        }
02348 
02349        *l = a;
02350 }
02351 
02352 void
02353 acl_append( AccessControl **l, AccessControl *a, int pos )
02354 {
02355        int i;
02356 
02357        for (i=0 ; i != pos && *l != NULL; l = &(*l)->acl_next, i++ ) {
02358               ;      /* Empty */
02359        }
02360        if ( *l && a )
02361               a->acl_next = *l;
02362        *l = a;
02363 }
02364 
02365 static void
02366 access_free( Access *a )
02367 {
02368        if ( !BER_BVISNULL( &a->a_dn_pat ) ) {
02369               free( a->a_dn_pat.bv_val );
02370        }
02371        if ( !BER_BVISNULL( &a->a_realdn_pat ) ) {
02372               free( a->a_realdn_pat.bv_val );
02373        }
02374        if ( !BER_BVISNULL( &a->a_peername_pat ) ) {
02375               free( a->a_peername_pat.bv_val );
02376        }
02377        if ( !BER_BVISNULL( &a->a_sockname_pat ) ) {
02378               free( a->a_sockname_pat.bv_val );
02379        }
02380        if ( !BER_BVISNULL( &a->a_domain_pat ) ) {
02381               free( a->a_domain_pat.bv_val );
02382        }
02383        if ( !BER_BVISNULL( &a->a_sockurl_pat ) ) {
02384               free( a->a_sockurl_pat.bv_val );
02385        }
02386        if ( !BER_BVISNULL( &a->a_set_pat ) ) {
02387               free( a->a_set_pat.bv_val );
02388        }
02389        if ( !BER_BVISNULL( &a->a_group_pat ) ) {
02390               free( a->a_group_pat.bv_val );
02391        }
02392 #ifdef SLAP_DYNACL
02393        if ( a->a_dynacl != NULL ) {
02394               slap_dynacl_t *da;
02395               for ( da = a->a_dynacl; da; ) {
02396                      slap_dynacl_t *tmp = da;
02397 
02398                      da = da->da_next;
02399 
02400                      if ( tmp->da_destroy ) {
02401                             tmp->da_destroy( tmp->da_private );
02402                      }
02403 
02404                      ch_free( tmp );
02405               }
02406        }
02407 #endif /* SLAP_DYNACL */
02408        free( a );
02409 }
02410 
02411 void
02412 acl_free( AccessControl *a )
02413 {
02414        Access *n;
02415        AttributeName *an;
02416 
02417        if ( a->acl_filter ) {
02418               filter_free( a->acl_filter );
02419        }
02420        if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
02421               if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
02422                      regfree( &a->acl_dn_re );
02423               }
02424               free ( a->acl_dn_pat.bv_val );
02425        }
02426        if ( a->acl_attrs ) {
02427               for ( an = a->acl_attrs; !BER_BVISNULL( &an->an_name ); an++ ) {
02428                      free( an->an_name.bv_val );
02429               }
02430               free( a->acl_attrs );
02431 
02432               if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
02433                      regfree( &a->acl_attrval_re );
02434               }
02435 
02436               if ( !BER_BVISNULL( &a->acl_attrval ) ) {
02437                      ber_memfree( a->acl_attrval.bv_val );
02438               }
02439        }
02440        for ( ; a->acl_access; a->acl_access = n ) {
02441               n = a->acl_access->a_next;
02442               access_free( a->acl_access );
02443        }
02444        free( a );
02445 }
02446 
02447 void
02448 acl_destroy( AccessControl *a )
02449 {
02450        AccessControl *n;
02451 
02452        for ( ; a; a = n ) {
02453               n = a->acl_next;
02454               acl_free( a );
02455        }
02456 
02457        if ( !BER_BVISNULL( &aclbuf ) ) {
02458               ch_free( aclbuf.bv_val );
02459               BER_BVZERO( &aclbuf );
02460        }
02461 }
02462 
02463 char *
02464 access2str( slap_access_t access )
02465 {
02466        if ( access == ACL_NONE ) {
02467               return "none";
02468 
02469        } else if ( access == ACL_DISCLOSE ) {
02470               return "disclose";
02471 
02472        } else if ( access == ACL_AUTH ) {
02473               return "auth";
02474 
02475        } else if ( access == ACL_COMPARE ) {
02476               return "compare";
02477 
02478        } else if ( access == ACL_SEARCH ) {
02479               return "search";
02480 
02481        } else if ( access == ACL_READ ) {
02482               return "read";
02483 
02484        } else if ( access == ACL_WRITE ) {
02485               return "write";
02486 
02487        } else if ( access == ACL_WADD ) {
02488               return "add";
02489 
02490        } else if ( access == ACL_WDEL ) {
02491               return "delete";
02492 
02493        } else if ( access == ACL_MANAGE ) {
02494               return "manage";
02495 
02496        }
02497 
02498        return "unknown";
02499 }
02500 
02501 slap_access_t
02502 str2access( const char *str )
02503 {
02504        if ( strcasecmp( str, "none" ) == 0 ) {
02505               return ACL_NONE;
02506 
02507        } else if ( strcasecmp( str, "disclose" ) == 0 ) {
02508               return ACL_DISCLOSE;
02509 
02510        } else if ( strcasecmp( str, "auth" ) == 0 ) {
02511               return ACL_AUTH;
02512 
02513        } else if ( strcasecmp( str, "compare" ) == 0 ) {
02514               return ACL_COMPARE;
02515 
02516        } else if ( strcasecmp( str, "search" ) == 0 ) {
02517               return ACL_SEARCH;
02518 
02519        } else if ( strcasecmp( str, "read" ) == 0 ) {
02520               return ACL_READ;
02521 
02522        } else if ( strcasecmp( str, "write" ) == 0 ) {
02523               return ACL_WRITE;
02524 
02525        } else if ( strcasecmp( str, "add" ) == 0 ) {
02526               return ACL_WADD;
02527 
02528        } else if ( strcasecmp( str, "delete" ) == 0 ) {
02529               return ACL_WDEL;
02530 
02531        } else if ( strcasecmp( str, "manage" ) == 0 ) {
02532               return ACL_MANAGE;
02533        }
02534 
02535        return( ACL_INVALID_ACCESS );
02536 }
02537 
02538 static char *
02539 safe_strncopy( char *ptr, const char *src, size_t n, struct berval *buf )
02540 {
02541        while ( ptr + n >= buf->bv_val + buf->bv_len ) {
02542               char *tmp = ch_realloc( buf->bv_val, 2*buf->bv_len );
02543               if ( tmp == NULL ) {
02544                      return NULL;
02545               }
02546               ptr = tmp + (ptr - buf->bv_val);
02547               buf->bv_val = tmp;
02548               buf->bv_len *= 2;
02549        }
02550 
02551        return lutil_strncopy( ptr, src, n );
02552 }
02553 
02554 static char *
02555 safe_strcopy( char *ptr, const char *s, struct berval *buf )
02556 {
02557        size_t n = strlen( s );
02558 
02559        return safe_strncopy( ptr, s, n, buf );
02560 }
02561 
02562 static char *
02563 safe_strbvcopy( char *ptr, const struct berval *bv, struct berval *buf )
02564 {
02565        return safe_strncopy( ptr, bv->bv_val, bv->bv_len, buf );
02566 }
02567 
02568 #define acl_safe_strcopy( ptr, s ) safe_strcopy( (ptr), (s), &aclbuf )
02569 #define acl_safe_strncopy( ptr, s, n ) safe_strncopy( (ptr), (s), (n), &aclbuf )
02570 #define acl_safe_strbvcopy( ptr, bv ) safe_strbvcopy( (ptr), (bv), &aclbuf )
02571 
02572 static char *
02573 dnaccess2text( slap_dn_access *bdn, char *ptr, int is_realdn )
02574 {
02575        *ptr++ = ' ';
02576 
02577        if ( is_realdn ) {
02578               ptr = acl_safe_strcopy( ptr, "real" );
02579        }
02580 
02581        if ( ber_bvccmp( &bdn->a_pat, '*' ) ||
02582               bdn->a_style == ACL_STYLE_ANONYMOUS ||
02583               bdn->a_style == ACL_STYLE_USERS ||
02584               bdn->a_style == ACL_STYLE_SELF )
02585        {
02586               if ( is_realdn ) {
02587                      assert( ! ber_bvccmp( &bdn->a_pat, '*' ) );
02588               }
02589                      
02590               ptr = acl_safe_strbvcopy( ptr, &bdn->a_pat );
02591               if ( bdn->a_style == ACL_STYLE_SELF && bdn->a_self_level != 0 ) {
02592                      char buf[SLAP_TEXT_BUFLEN];
02593                      int n = snprintf( buf, sizeof(buf), ".level{%d}", bdn->a_self_level );
02594                      if ( n > 0 ) {
02595                             ptr = acl_safe_strncopy( ptr, buf, n );
02596                      } /* else ? */
02597               }
02598 
02599        } else {
02600               ptr = acl_safe_strcopy( ptr, "dn." );
02601               if ( bdn->a_style == ACL_STYLE_BASE )
02602                      ptr = acl_safe_strcopy( ptr, style_base );
02603               else 
02604                      ptr = acl_safe_strcopy( ptr, style_strings[bdn->a_style] );
02605               if ( bdn->a_style == ACL_STYLE_LEVEL ) {
02606                      char buf[SLAP_TEXT_BUFLEN];
02607                      int n = snprintf( buf, sizeof(buf), "{%d}", bdn->a_level );
02608                      if ( n > 0 ) {
02609                             ptr = acl_safe_strncopy( ptr, buf, n );
02610                      } /* else ? */
02611               }
02612               if ( bdn->a_expand ) {
02613                      ptr = acl_safe_strcopy( ptr, ",expand" );
02614               }
02615               ptr = acl_safe_strcopy( ptr, "=\"" );
02616               ptr = acl_safe_strbvcopy( ptr, &bdn->a_pat );
02617               ptr = acl_safe_strcopy( ptr, "\"" );
02618        }
02619        return ptr;
02620 }
02621 
02622 static char *
02623 access2text( Access *b, char *ptr )
02624 {
02625        char maskbuf[ACCESSMASK_MAXLEN];
02626 
02627        ptr = acl_safe_strcopy( ptr, "\tby" );
02628 
02629        if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
02630               ptr = dnaccess2text( &b->a_dn, ptr, 0 );
02631        }
02632        if ( b->a_dn_at ) {
02633               ptr = acl_safe_strcopy( ptr, " dnattr=" );
02634               ptr = acl_safe_strbvcopy( ptr, &b->a_dn_at->ad_cname );
02635        }
02636 
02637        if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
02638               ptr = dnaccess2text( &b->a_realdn, ptr, 1 );
02639        }
02640        if ( b->a_realdn_at ) {
02641               ptr = acl_safe_strcopy( ptr, " realdnattr=" );
02642               ptr = acl_safe_strbvcopy( ptr, &b->a_realdn_at->ad_cname );
02643        }
02644 
02645        if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
02646               ptr = acl_safe_strcopy( ptr, " group/" );
02647               ptr = acl_safe_strcopy( ptr, b->a_group_oc ?
02648                      b->a_group_oc->soc_cname.bv_val : SLAPD_GROUP_CLASS );
02649               ptr = acl_safe_strcopy( ptr, "/" );
02650               ptr = acl_safe_strcopy( ptr, b->a_group_at ?
02651                      b->a_group_at->ad_cname.bv_val : SLAPD_GROUP_ATTR );
02652               ptr = acl_safe_strcopy( ptr, "." );
02653               ptr = acl_safe_strcopy( ptr, style_strings[b->a_group_style] );
02654               ptr = acl_safe_strcopy( ptr, "=\"" );
02655               ptr = acl_safe_strbvcopy( ptr, &b->a_group_pat );
02656               ptr = acl_safe_strcopy( ptr, "\"" );
02657        }
02658 
02659        if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
02660               ptr = acl_safe_strcopy( ptr, " peername" );
02661               ptr = acl_safe_strcopy( ptr, "." );
02662               ptr = acl_safe_strcopy( ptr, style_strings[b->a_peername_style] );
02663               ptr = acl_safe_strcopy( ptr, "=\"" );
02664               ptr = acl_safe_strbvcopy( ptr, &b->a_peername_pat );
02665               ptr = acl_safe_strcopy( ptr, "\"" );
02666        }
02667 
02668        if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
02669               ptr = acl_safe_strcopy( ptr, " sockname" );
02670               ptr = acl_safe_strcopy( ptr, "." );
02671               ptr = acl_safe_strcopy( ptr, style_strings[b->a_sockname_style] );
02672               ptr = acl_safe_strcopy( ptr, "=\"" );
02673               ptr = acl_safe_strbvcopy( ptr, &b->a_sockname_pat );
02674               ptr = acl_safe_strcopy( ptr, "\"" );
02675        }
02676 
02677        if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
02678               ptr = acl_safe_strcopy( ptr, " domain" );
02679               ptr = acl_safe_strcopy( ptr, "." );
02680               ptr = acl_safe_strcopy( ptr, style_strings[b->a_domain_style] );
02681               if ( b->a_domain_expand ) {
02682                      ptr = acl_safe_strcopy( ptr, ",expand" );
02683               }
02684               ptr = acl_safe_strcopy( ptr, "=" );
02685               ptr = acl_safe_strbvcopy( ptr, &b->a_domain_pat );
02686        }
02687 
02688        if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
02689               ptr = acl_safe_strcopy( ptr, " sockurl" );
02690               ptr = acl_safe_strcopy( ptr, "." );
02691               ptr = acl_safe_strcopy( ptr, style_strings[b->a_sockurl_style] );
02692               ptr = acl_safe_strcopy( ptr, "=\"" );
02693               ptr = acl_safe_strbvcopy( ptr, &b->a_sockurl_pat );
02694               ptr = acl_safe_strcopy( ptr, "\"" );
02695        }
02696 
02697        if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
02698               ptr = acl_safe_strcopy( ptr, " set" );
02699               ptr = acl_safe_strcopy( ptr, "." );
02700               ptr = acl_safe_strcopy( ptr, style_strings[b->a_set_style] );
02701               ptr = acl_safe_strcopy( ptr, "=\"" );
02702               ptr = acl_safe_strbvcopy( ptr, &b->a_set_pat );
02703               ptr = acl_safe_strcopy( ptr, "\"" );
02704        }
02705 
02706 #ifdef SLAP_DYNACL
02707        if ( b->a_dynacl ) {
02708               slap_dynacl_t *da;
02709 
02710               for ( da = b->a_dynacl; da; da = da->da_next ) {
02711                      if ( da->da_unparse ) {
02712                             struct berval bv = BER_BVNULL;
02713                             (void)( *da->da_unparse )( da->da_private, &bv );
02714                             assert( !BER_BVISNULL( &bv ) );
02715                             ptr = acl_safe_strbvcopy( ptr, &bv );
02716                             ch_free( bv.bv_val );
02717                      }
02718               }
02719        }
02720 #endif /* SLAP_DYNACL */
02721 
02722        /* Security Strength Factors */
02723        if ( b->a_authz.sai_ssf ) {
02724               char buf[SLAP_TEXT_BUFLEN];
02725               int n = snprintf( buf, sizeof(buf), " ssf=%u", 
02726                      b->a_authz.sai_ssf );
02727               ptr = acl_safe_strncopy( ptr, buf, n );
02728        }
02729        if ( b->a_authz.sai_transport_ssf ) {
02730               char buf[SLAP_TEXT_BUFLEN];
02731               int n = snprintf( buf, sizeof(buf), " transport_ssf=%u",
02732                      b->a_authz.sai_transport_ssf );
02733               ptr = acl_safe_strncopy( ptr, buf, n );
02734        }
02735        if ( b->a_authz.sai_tls_ssf ) {
02736               char buf[SLAP_TEXT_BUFLEN];
02737               int n = snprintf( buf, sizeof(buf), " tls_ssf=%u",
02738                      b->a_authz.sai_tls_ssf );
02739               ptr = acl_safe_strncopy( ptr, buf, n );
02740        }
02741        if ( b->a_authz.sai_sasl_ssf ) {
02742               char buf[SLAP_TEXT_BUFLEN];
02743               int n = snprintf( buf, sizeof(buf), " sasl_ssf=%u",
02744                      b->a_authz.sai_sasl_ssf );
02745               ptr = acl_safe_strncopy( ptr, buf, n );
02746        }
02747 
02748        ptr = acl_safe_strcopy( ptr, " " );
02749        if ( b->a_dn_self ) {
02750               ptr = acl_safe_strcopy( ptr, "self" );
02751        } else if ( b->a_realdn_self ) {
02752               ptr = acl_safe_strcopy( ptr, "realself" );
02753        }
02754        ptr = acl_safe_strcopy( ptr, accessmask2str( b->a_access_mask, maskbuf, 0 ));
02755        if ( !maskbuf[0] ) ptr--;
02756 
02757        if( b->a_type == ACL_BREAK ) {
02758               ptr = acl_safe_strcopy( ptr, " break" );
02759 
02760        } else if( b->a_type == ACL_CONTINUE ) {
02761               ptr = acl_safe_strcopy( ptr, " continue" );
02762 
02763        } else if( b->a_type != ACL_STOP ) {
02764               ptr = acl_safe_strcopy( ptr, " unknown-control" );
02765        } else {
02766               if ( !maskbuf[0] ) ptr = acl_safe_strcopy( ptr, " stop" );
02767        }
02768        ptr = acl_safe_strcopy( ptr, "\n" );
02769 
02770        return ptr;
02771 }
02772 
02773 void
02774 acl_unparse( AccessControl *a, struct berval *bv )
02775 {
02776        Access *b;
02777        char   *ptr;
02778        int    to = 0;
02779 
02780        if ( BER_BVISNULL( &aclbuf ) ) {
02781               aclbuf.bv_val = ch_malloc( ACLBUF_CHUNKSIZE );
02782               aclbuf.bv_len = ACLBUF_CHUNKSIZE;
02783        }
02784 
02785        bv->bv_len = 0;
02786 
02787        ptr = aclbuf.bv_val;
02788 
02789        ptr = acl_safe_strcopy( ptr, "to" );
02790        if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
02791               to++;
02792               ptr = acl_safe_strcopy( ptr, " dn." );
02793               if ( a->acl_dn_style == ACL_STYLE_BASE )
02794                      ptr = acl_safe_strcopy( ptr, style_base );
02795               else
02796                      ptr = acl_safe_strcopy( ptr, style_strings[a->acl_dn_style] );
02797               ptr = acl_safe_strcopy( ptr, "=\"" );
02798               ptr = acl_safe_strbvcopy( ptr, &a->acl_dn_pat );
02799               ptr = acl_safe_strcopy( ptr, "\"\n" );
02800        }
02801 
02802        if ( a->acl_filter != NULL ) {
02803               struct berval fbv = BER_BVNULL;
02804 
02805               to++;
02806               filter2bv( a->acl_filter, &fbv );
02807               ptr = acl_safe_strcopy( ptr, " filter=\"" );
02808               ptr = acl_safe_strbvcopy( ptr, &fbv );
02809               ptr = acl_safe_strcopy( ptr, "\"\n" );
02810               ch_free( fbv.bv_val );
02811        }
02812 
02813        if ( a->acl_attrs != NULL ) {
02814               int    first = 1;
02815               AttributeName *an;
02816               to++;
02817 
02818               ptr = acl_safe_strcopy( ptr, " attrs=" );
02819               for ( an = a->acl_attrs; an && !BER_BVISNULL( &an->an_name ); an++ ) {
02820                      if ( ! first ) ptr = acl_safe_strcopy( ptr, ",");
02821                      if (an->an_oc) {
02822                             ptr = acl_safe_strcopy( ptr, ( an->an_flags & SLAP_AN_OCEXCLUDE ) ? "!" : "@" );
02823                             ptr = acl_safe_strbvcopy( ptr, &an->an_oc->soc_cname );
02824 
02825                      } else {
02826                             ptr = acl_safe_strbvcopy( ptr, &an->an_name );
02827                      }
02828                      first = 0;
02829               }
02830               ptr = acl_safe_strcopy( ptr, "\n" );
02831        }
02832 
02833        if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
02834               to++;
02835               ptr = acl_safe_strcopy( ptr, " val." );
02836               if ( a->acl_attrval_style == ACL_STYLE_BASE &&
02837                      a->acl_attrs[0].an_desc->ad_type->sat_syntax ==
02838                             slap_schema.si_syn_distinguishedName )
02839                      ptr = acl_safe_strcopy( ptr, style_base );
02840               else
02841                      ptr = acl_safe_strcopy( ptr, style_strings[a->acl_attrval_style] );
02842               ptr = acl_safe_strcopy( ptr, "=\"" );
02843               ptr = acl_safe_strbvcopy( ptr, &a->acl_attrval );
02844               ptr = acl_safe_strcopy( ptr, "\"\n" );
02845        }
02846 
02847        if ( !to ) {
02848               ptr = acl_safe_strcopy( ptr, " *\n" );
02849        }
02850 
02851        for ( b = a->acl_access; b != NULL; b = b->a_next ) {
02852               ptr = access2text( b, ptr );
02853        }
02854        *ptr = '\0';
02855        bv->bv_val = aclbuf.bv_val;
02856        bv->bv_len = ptr - bv->bv_val;
02857 }
02858 
02859 #ifdef LDAP_DEBUG
02860 static void
02861 print_acl( Backend *be, AccessControl *a )
02862 {
02863        struct berval bv;
02864 
02865        acl_unparse( a, &bv );
02866        fprintf( stderr, "%s ACL: access %s\n",
02867               be == NULL ? "Global" : "Backend", bv.bv_val );
02868 }
02869 #endif /* LDAP_DEBUG */