Back to index

openldap  2.4.31
saslauthz.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1998-2012 The OpenLDAP Foundation.
00005  * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
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 
00017 #include "portable.h"
00018 
00019 #include <stdio.h>
00020 #ifdef HAVE_LIMITS_H
00021 #include <limits.h>
00022 #endif
00023 
00024 #include <ac/stdlib.h>
00025 #include <ac/string.h>
00026 #include <ac/ctype.h>
00027 
00028 #include "slap.h"
00029 
00030 #include "lutil.h"
00031 
00032 #define SASLREGEX_REPLACE 10
00033 
00034 #define LDAP_X_SCOPE_EXACT  ((ber_int_t) 0x0010)
00035 #define LDAP_X_SCOPE_REGEX  ((ber_int_t) 0x0020)
00036 #define LDAP_X_SCOPE_CHILDREN      ((ber_int_t) 0x0030)
00037 #define LDAP_X_SCOPE_SUBTREE       ((ber_int_t) 0x0040)
00038 #define LDAP_X_SCOPE_ONELEVEL      ((ber_int_t) 0x0050)
00039 #define LDAP_X_SCOPE_GROUP  ((ber_int_t) 0x0060)
00040 #define LDAP_X_SCOPE_USERS  ((ber_int_t) 0x0070)
00041 
00042 /*
00043  * IDs in DNauthzid form can now have a type specifier, that
00044  * influences how they are used in related operations.
00045  *
00046  * syntax: dn[.{exact|regex}]:<val>
00047  *
00048  * dn.exact:  the value must pass normalization and is used 
00049  *            in exact DN match.
00050  * dn.regex:  the value is treated as a regular expression 
00051  *            in matching DN values in authz{To|From}
00052  *            attributes.
00053  * dn:        for backwards compatibility reasons, the value 
00054  *            is treated as a regular expression, and thus 
00055  *            it is not normalized nor validated; it is used
00056  *            in exact or regex comparisons based on the 
00057  *            context.
00058  *
00059  * IDs in DNauthzid form can now have a type specifier, that
00060  * influences how they are used in related operations.
00061  *
00062  * syntax: u[.mech[/realm]]:<val>
00063  * 
00064  * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
00065  * and realm is mechanism specific realm (separate to those
00066  * which are representable as part of the principal).
00067  */
00068 
00069 typedef struct sasl_regexp {
00070   char *sr_match;                                       /* regexp match pattern */
00071   char *sr_replace;                              /* regexp replace pattern */
00072   regex_t sr_workspace;                                 /* workspace for regexp engine */
00073   int sr_offset[SASLREGEX_REPLACE+2];     /* offsets of $1,$2... in *replace */
00074 } SaslRegexp_t;
00075 
00076 static int nSaslRegexp = 0;
00077 static SaslRegexp_t *SaslRegexp = NULL;
00078 
00079 #ifdef SLAP_AUTH_REWRITE
00080 #include "rewrite.h"
00081 struct rewrite_info  *sasl_rwinfo = NULL;
00082 #define AUTHID_CONTEXT      "authid"
00083 #endif /* SLAP_AUTH_REWRITE */
00084 
00085 /* What SASL proxy authorization policies are allowed? */
00086 #define       SASL_AUTHZ_NONE      0x00
00087 #define       SASL_AUTHZ_FROM      0x01
00088 #define       SASL_AUTHZ_TO 0x02
00089 #define SASL_AUTHZ_AND      0x10
00090 
00091 static const char *policy_txt[] = {
00092        "none", "from", "to", "any"
00093 };
00094 
00095 static int authz_policy = SASL_AUTHZ_NONE;
00096 
00097 static int
00098 slap_sasl_match( Operation *opx, struct berval *rule,
00099        struct berval *assertDN, struct berval *authc );
00100 
00101 int slap_sasl_setpolicy( const char *arg )
00102 {
00103        int rc = LDAP_SUCCESS;
00104 
00105        if ( strcasecmp( arg, "none" ) == 0 ) {
00106               authz_policy = SASL_AUTHZ_NONE;
00107        } else if ( strcasecmp( arg, "from" ) == 0 ) {
00108               authz_policy = SASL_AUTHZ_FROM;
00109        } else if ( strcasecmp( arg, "to" ) == 0 ) {
00110               authz_policy = SASL_AUTHZ_TO;
00111        } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
00112               authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
00113        } else if ( strcasecmp( arg, "all" ) == 0 ) {
00114               authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
00115        } else {
00116               rc = LDAP_OTHER;
00117        }
00118        return rc;
00119 }
00120 
00121 const char * slap_sasl_getpolicy()
00122 {
00123        if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
00124               return "all";
00125        else
00126               return policy_txt[authz_policy];
00127 }
00128 
00129 int slap_parse_user( struct berval *id, struct berval *user,
00130               struct berval *realm, struct berval *mech )
00131 {
00132        char   u;
00133        
00134        assert( id != NULL );
00135        assert( !BER_BVISNULL( id ) );
00136        assert( user != NULL );
00137        assert( realm != NULL );
00138        assert( mech != NULL );
00139 
00140        u = id->bv_val[ 0 ];
00141        
00142        if ( u != 'u' && u != 'U' ) {
00143               /* called with something other than u: */
00144               return LDAP_PROTOCOL_ERROR;
00145        }
00146 
00147        /* uauthzid form:
00148         *            u[.mech[/realm]]:user
00149         */
00150        
00151        user->bv_val = ber_bvchr( id, ':' );
00152        if ( BER_BVISNULL( user ) ) {
00153               return LDAP_PROTOCOL_ERROR;
00154        }
00155        user->bv_val[ 0 ] = '\0';
00156        user->bv_val++;
00157        user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
00158 
00159        mech->bv_val = ber_bvchr( id, '.' );
00160        if ( !BER_BVISNULL( mech ) ) {
00161               mech->bv_val[ 0 ] = '\0';
00162               mech->bv_val++;
00163               mech->bv_len = user->bv_val - mech->bv_val - 1;
00164 
00165               realm->bv_val = ber_bvchr( mech, '/' );
00166 
00167               if ( !BER_BVISNULL( realm ) ) {
00168                      realm->bv_val[ 0 ] = '\0';
00169                      realm->bv_val++;
00170                      mech->bv_len = realm->bv_val - mech->bv_val - 1;
00171                      realm->bv_len = user->bv_val - realm->bv_val - 1;
00172               }
00173 
00174        } else {
00175               BER_BVZERO( realm );
00176        }
00177 
00178        if ( id->bv_val[ 1 ] != '\0' ) {
00179               return LDAP_PROTOCOL_ERROR;
00180        }
00181 
00182        if ( !BER_BVISNULL( mech ) ) {
00183               assert( mech->bv_val == id->bv_val + 2 );
00184 
00185               AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
00186               mech->bv_val -= 2;
00187        }
00188 
00189        if ( !BER_BVISNULL( realm ) ) {
00190               assert( realm->bv_val >= id->bv_val + 2 );
00191 
00192               AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
00193               realm->bv_val -= 2;
00194        }
00195 
00196        /* leave "u:" before user */
00197        user->bv_val -= 2;
00198        user->bv_len += 2;
00199        user->bv_val[ 0 ] = u;
00200        user->bv_val[ 1 ] = ':';
00201 
00202        return LDAP_SUCCESS;
00203 }
00204 
00205 int
00206 authzValidate(
00207        Syntax *syntax,
00208        struct berval *in )
00209 {
00210        struct berval bv;
00211        int           rc = LDAP_INVALID_SYNTAX;
00212        LDAPURLDesc   *ludp = NULL;
00213        int           scope = -1;
00214 
00215        /*
00216         * 1) <DN>
00217         * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
00218         * 3) dn.regex:<pattern>
00219         * 4) u[.mech[/realm]]:<ID>
00220         * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
00221         * 6) <URL>
00222         */
00223 
00224        assert( in != NULL );
00225        assert( !BER_BVISNULL( in ) );
00226 
00227        Debug( LDAP_DEBUG_TRACE,
00228               "authzValidate: parsing %s\n", in->bv_val, 0, 0 );
00229 
00230        /*
00231         * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
00232         * 3) dn.regex:<pattern>
00233         *
00234         * <DN> must pass DN normalization
00235         */
00236        if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
00237               bv.bv_val = in->bv_val + STRLENOF( "dn" );
00238 
00239               if ( bv.bv_val[ 0 ] == '.' ) {
00240                      bv.bv_val++;
00241 
00242                      if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
00243                             bv.bv_val += STRLENOF( "exact:" );
00244                             scope = LDAP_X_SCOPE_EXACT;
00245 
00246                      } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
00247                             bv.bv_val += STRLENOF( "regex:" );
00248                             scope = LDAP_X_SCOPE_REGEX;
00249 
00250                      } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
00251                             bv.bv_val += STRLENOF( "children:" );
00252                             scope = LDAP_X_SCOPE_CHILDREN;
00253 
00254                      } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
00255                             bv.bv_val += STRLENOF( "subtree:" );
00256                             scope = LDAP_X_SCOPE_SUBTREE;
00257 
00258                      } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
00259                             bv.bv_val += STRLENOF( "onelevel:" );
00260                             scope = LDAP_X_SCOPE_ONELEVEL;
00261 
00262                      } else {
00263                             return LDAP_INVALID_SYNTAX;
00264                      }
00265 
00266               } else {
00267                      if ( bv.bv_val[ 0 ] != ':' ) {
00268                             return LDAP_INVALID_SYNTAX;
00269                      }
00270                      scope = LDAP_X_SCOPE_EXACT;
00271                      bv.bv_val++;
00272               }
00273 
00274               bv.bv_val += strspn( bv.bv_val, " " );
00275               /* jump here in case no type specification was present
00276                * and uri was not an URI... HEADS-UP: assuming EXACT */
00277 is_dn:        bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
00278 
00279               /* a single '*' means any DN without using regexes */
00280               if ( ber_bvccmp( &bv, '*' ) ) {
00281                      /* LDAP_X_SCOPE_USERS */
00282                      return LDAP_SUCCESS;
00283               }
00284 
00285               switch ( scope ) {
00286               case LDAP_X_SCOPE_EXACT:
00287               case LDAP_X_SCOPE_CHILDREN:
00288               case LDAP_X_SCOPE_SUBTREE:
00289               case LDAP_X_SCOPE_ONELEVEL:
00290                      return dnValidate( NULL, &bv );
00291 
00292               case LDAP_X_SCOPE_REGEX:
00293                      return LDAP_SUCCESS;
00294               }
00295 
00296               return rc;
00297 
00298        /*
00299         * 4) u[.mech[/realm]]:<ID>
00300         */
00301        } else if ( ( in->bv_val[ 0 ] == 'u' || in->bv_val[ 0 ] == 'U' )
00302                      && ( in->bv_val[ 1 ] == ':' 
00303                             || in->bv_val[ 1 ] == '/' 
00304                             || in->bv_val[ 1 ] == '.' ) )
00305        {
00306               char          buf[ SLAP_LDAPDN_MAXLEN ];
00307               struct berval id,
00308                             user = BER_BVNULL,
00309                             realm = BER_BVNULL,
00310                             mech = BER_BVNULL;
00311 
00312               if ( sizeof( buf ) <= in->bv_len ) {
00313                      return LDAP_INVALID_SYNTAX;
00314               }
00315 
00316               id.bv_len = in->bv_len;
00317               id.bv_val = buf;
00318               strncpy( buf, in->bv_val, sizeof( buf ) );
00319 
00320               rc = slap_parse_user( &id, &user, &realm, &mech );
00321               if ( rc != LDAP_SUCCESS ) {
00322                      return LDAP_INVALID_SYNTAX;
00323               }
00324 
00325               return rc;
00326 
00327        /*
00328         * 5) group[/groupClass[/memberAttr]]:<DN>
00329         *
00330         * <groupClass> defaults to "groupOfNames"
00331         * <memberAttr> defaults to "member"
00332         * 
00333         * <DN> must pass DN normalization
00334         */
00335        } else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
00336        {
00337               struct berval group_dn = BER_BVNULL,
00338                             group_oc = BER_BVNULL,
00339                             member_at = BER_BVNULL;
00340 
00341               bv.bv_val = in->bv_val + STRLENOF( "group" );
00342               bv.bv_len = in->bv_len - STRLENOF( "group" );
00343               group_dn.bv_val = ber_bvchr( &bv, ':' );
00344               if ( group_dn.bv_val == NULL ) {
00345                      /* last chance: assume it's a(n exact) DN ... */
00346                      bv.bv_val = in->bv_val;
00347                      scope = LDAP_X_SCOPE_EXACT;
00348                      goto is_dn;
00349               }
00350               
00351               /*
00352                * FIXME: we assume that "member" and "groupOfNames"
00353                * are present in schema...
00354                */
00355               if ( bv.bv_val[ 0 ] == '/' ) {
00356                      group_oc.bv_val = &bv.bv_val[ 1 ];
00357                      group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
00358 
00359                      member_at.bv_val = ber_bvchr( &group_oc, '/' );
00360                      if ( member_at.bv_val ) {
00361                             AttributeDescription *ad = NULL;
00362                             const char           *text = NULL;
00363 
00364                             group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
00365                             member_at.bv_val++;
00366                             member_at.bv_len = group_dn.bv_val - member_at.bv_val;
00367                             rc = slap_bv2ad( &member_at, &ad, &text );
00368                             if ( rc != LDAP_SUCCESS ) {
00369                                    return rc;
00370                             }
00371                      }
00372 
00373                      if ( oc_bvfind( &group_oc ) == NULL ) {
00374                             return LDAP_INVALID_SYNTAX;
00375                      }
00376               }
00377 
00378               group_dn.bv_val++;
00379               group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
00380 
00381               rc = dnValidate( NULL, &group_dn );
00382               if ( rc != LDAP_SUCCESS ) {
00383                      return rc;
00384               }
00385 
00386               return rc;
00387        }
00388 
00389        /*
00390         * ldap:///<base>??<scope>?<filter>
00391         * <scope> ::= {base|one|subtree}
00392         *
00393         * <scope> defaults to "base"
00394         * <base> must pass DN normalization
00395         * <filter> must pass str2filter()
00396         */
00397        rc = ldap_url_parse( in->bv_val, &ludp );
00398        switch ( rc ) {
00399        case LDAP_URL_SUCCESS:
00400               /* FIXME: the check is pedantic, but I think it's necessary,
00401                * because people tend to use things like ldaps:// which
00402                * gives the idea SSL is being used.  Maybe we could
00403                * accept ldapi:// as well, but the point is that we use
00404                * an URL as an easy means to define bits of a search with
00405                * little parsing.
00406                */
00407               if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
00408                      /*
00409                       * must be ldap:///
00410                       */
00411                      rc = LDAP_INVALID_SYNTAX;
00412                      goto done;
00413               }
00414               break;
00415 
00416        case LDAP_URL_ERR_BADSCHEME:
00417               /*
00418                * last chance: assume it's a(n exact) DN ...
00419                *
00420                * NOTE: must pass DN normalization
00421                */
00422               ldap_free_urldesc( ludp );
00423               bv.bv_val = in->bv_val;
00424               scope = LDAP_X_SCOPE_EXACT;
00425               goto is_dn;
00426 
00427        default:
00428               rc = LDAP_INVALID_SYNTAX;
00429               goto done;
00430        }
00431 
00432        if ( ( ludp->lud_host && *ludp->lud_host )
00433               || ludp->lud_attrs || ludp->lud_exts )
00434        {
00435               /* host part must be empty */
00436               /* attrs and extensions parts must be empty */
00437               rc = LDAP_INVALID_SYNTAX;
00438               goto done;
00439        }
00440 
00441        /* Grab the filter */
00442        if ( ludp->lud_filter ) {
00443               Filter *f = str2filter( ludp->lud_filter );
00444               if ( f == NULL ) {
00445                      rc = LDAP_INVALID_SYNTAX;
00446                      goto done;
00447               }
00448               filter_free( f );
00449        }
00450 
00451        /* Grab the searchbase */
00452        assert( ludp->lud_dn != NULL );
00453        ber_str2bv( ludp->lud_dn, 0, 0, &bv );
00454        rc = dnValidate( NULL, &bv );
00455 
00456 done:
00457        ldap_free_urldesc( ludp );
00458        return( rc );
00459 }
00460 
00461 static int
00462 authzPrettyNormal(
00463        struct berval *val,
00464        struct berval *normalized,
00465        void          *ctx,
00466        int           normalize )
00467 {
00468        struct berval bv;
00469        int           rc = LDAP_INVALID_SYNTAX;
00470        LDAPURLDesc   *ludp = NULL;
00471        char          *lud_dn = NULL,
00472                      *lud_filter = NULL;
00473        int           scope = -1;
00474 
00475        /*
00476         * 1) <DN>
00477         * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
00478         * 3) dn.regex:<pattern>
00479         * 4) u[.mech[/realm]]:<ID>
00480         * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
00481         * 6) <URL>
00482         */
00483 
00484        assert( val != NULL );
00485        assert( !BER_BVISNULL( val ) );
00486 
00487        /*
00488         * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
00489         * 3) dn.regex:<pattern>
00490         *
00491         * <DN> must pass DN normalization
00492         */
00493        if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
00494               struct berval out = BER_BVNULL,
00495                             prefix = BER_BVNULL;
00496               char          *ptr;
00497 
00498               bv.bv_val = val->bv_val + STRLENOF( "dn" );
00499 
00500               if ( bv.bv_val[ 0 ] == '.' ) {
00501                      bv.bv_val++;
00502 
00503                      if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
00504                             bv.bv_val += STRLENOF( "exact:" );
00505                             scope = LDAP_X_SCOPE_EXACT;
00506 
00507                      } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
00508                             bv.bv_val += STRLENOF( "regex:" );
00509                             scope = LDAP_X_SCOPE_REGEX;
00510 
00511                      } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
00512                             bv.bv_val += STRLENOF( "children:" );
00513                             scope = LDAP_X_SCOPE_CHILDREN;
00514 
00515                      } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
00516                             bv.bv_val += STRLENOF( "subtree:" );
00517                             scope = LDAP_X_SCOPE_SUBTREE;
00518 
00519                      } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
00520                             bv.bv_val += STRLENOF( "onelevel:" );
00521                             scope = LDAP_X_SCOPE_ONELEVEL;
00522 
00523                      } else {
00524                             return LDAP_INVALID_SYNTAX;
00525                      }
00526 
00527               } else {
00528                      if ( bv.bv_val[ 0 ] != ':' ) {
00529                             return LDAP_INVALID_SYNTAX;
00530                      }
00531                      scope = LDAP_X_SCOPE_EXACT;
00532                      bv.bv_val++;
00533               }
00534 
00535               bv.bv_val += strspn( bv.bv_val, " " );
00536               /* jump here in case no type specification was present
00537                * and uri was not an URI... HEADS-UP: assuming EXACT */
00538 is_dn:        bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
00539 
00540               /* a single '*' means any DN without using regexes */
00541               if ( ber_bvccmp( &bv, '*' ) ) {
00542                      ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
00543                      return LDAP_SUCCESS;
00544               }
00545 
00546               switch ( scope ) {
00547               case LDAP_X_SCOPE_EXACT:
00548               case LDAP_X_SCOPE_CHILDREN:
00549               case LDAP_X_SCOPE_SUBTREE:
00550               case LDAP_X_SCOPE_ONELEVEL:
00551                      if ( normalize ) {
00552                             rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
00553                      } else {
00554                             rc = dnPretty( NULL, &bv, &out, ctx );
00555                      }
00556                      if( rc != LDAP_SUCCESS ) {
00557                             return LDAP_INVALID_SYNTAX;
00558                      }
00559                      break;
00560 
00561               case LDAP_X_SCOPE_REGEX:
00562                      normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
00563                      normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
00564                      ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
00565                      ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
00566                      ptr[ 0 ] = '\0';
00567                      return LDAP_SUCCESS;
00568 
00569               default:
00570                      return LDAP_INVALID_SYNTAX;
00571               }
00572 
00573               /* prepare prefix */
00574               switch ( scope ) {
00575               case LDAP_X_SCOPE_EXACT:
00576                      BER_BVSTR( &prefix, "dn:" );
00577                      break;
00578 
00579               case LDAP_X_SCOPE_CHILDREN:
00580                      BER_BVSTR( &prefix, "dn.children:" );
00581                      break;
00582 
00583               case LDAP_X_SCOPE_SUBTREE:
00584                      BER_BVSTR( &prefix, "dn.subtree:" );
00585                      break;
00586 
00587               case LDAP_X_SCOPE_ONELEVEL:
00588                      BER_BVSTR( &prefix, "dn.onelevel:" );
00589                      break;
00590 
00591               default:
00592                      assert( 0 );
00593                      break;
00594               }
00595 
00596               normalized->bv_len = prefix.bv_len + out.bv_len;
00597               normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
00598               
00599               ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
00600               ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
00601               ptr[ 0 ] = '\0';
00602               ber_memfree_x( out.bv_val, ctx );
00603 
00604               return LDAP_SUCCESS;
00605 
00606        /*
00607         * 4) u[.mech[/realm]]:<ID>
00608         */
00609        } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
00610                      && ( val->bv_val[ 1 ] == ':' 
00611                             || val->bv_val[ 1 ] == '/' 
00612                             || val->bv_val[ 1 ] == '.' ) )
00613        {
00614               char          buf[ SLAP_LDAPDN_MAXLEN ];
00615               struct berval id,
00616                             user = BER_BVNULL,
00617                             realm = BER_BVNULL,
00618                             mech = BER_BVNULL;
00619 
00620               if ( sizeof( buf ) <= val->bv_len ) {
00621                      return LDAP_INVALID_SYNTAX;
00622               }
00623 
00624               id.bv_len = val->bv_len;
00625               id.bv_val = buf;
00626               strncpy( buf, val->bv_val, sizeof( buf ) );
00627 
00628               rc = slap_parse_user( &id, &user, &realm, &mech );
00629               if ( rc != LDAP_SUCCESS ) {
00630                      return LDAP_INVALID_SYNTAX;
00631               }
00632 
00633               ber_dupbv_x( normalized, val, ctx );
00634 
00635               return rc;
00636 
00637        /*
00638         * 5) group[/groupClass[/memberAttr]]:<DN>
00639         *
00640         * <groupClass> defaults to "groupOfNames"
00641         * <memberAttr> defaults to "member"
00642         * 
00643         * <DN> must pass DN normalization
00644         */
00645        } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
00646        {
00647               struct berval group_dn = BER_BVNULL,
00648                             group_oc = BER_BVNULL,
00649                             member_at = BER_BVNULL,
00650                             out = BER_BVNULL;
00651               char          *ptr;
00652 
00653               bv.bv_val = val->bv_val + STRLENOF( "group" );
00654               bv.bv_len = val->bv_len - STRLENOF( "group" );
00655               group_dn.bv_val = ber_bvchr( &bv, ':' );
00656               if ( group_dn.bv_val == NULL ) {
00657                      /* last chance: assume it's a(n exact) DN ... */
00658                      bv.bv_val = val->bv_val;
00659                      scope = LDAP_X_SCOPE_EXACT;
00660                      goto is_dn;
00661               }
00662 
00663               /*
00664                * FIXME: we assume that "member" and "groupOfNames"
00665                * are present in schema...
00666                */
00667               if ( bv.bv_val[ 0 ] == '/' ) {
00668                      ObjectClass          *oc = NULL;
00669 
00670                      group_oc.bv_val = &bv.bv_val[ 1 ];
00671                      group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
00672 
00673                      member_at.bv_val = ber_bvchr( &group_oc, '/' );
00674                      if ( member_at.bv_val ) {
00675                             AttributeDescription *ad = NULL;
00676                             const char           *text = NULL;
00677 
00678                             group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
00679                             member_at.bv_val++;
00680                             member_at.bv_len = group_dn.bv_val - member_at.bv_val;
00681                             rc = slap_bv2ad( &member_at, &ad, &text );
00682                             if ( rc != LDAP_SUCCESS ) {
00683                                    return rc;
00684                             }
00685 
00686                             member_at = ad->ad_cname;
00687 
00688                      }
00689 
00690                      oc = oc_bvfind( &group_oc );
00691                      if ( oc == NULL ) {
00692                             return LDAP_INVALID_SYNTAX;
00693                      }
00694 
00695                      group_oc = oc->soc_cname;
00696               }
00697 
00698               group_dn.bv_val++;
00699               group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
00700 
00701               if ( normalize ) {
00702                      rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
00703               } else {
00704                      rc = dnPretty( NULL, &group_dn, &out, ctx );
00705               }
00706               if ( rc != LDAP_SUCCESS ) {
00707                      return rc;
00708               }
00709 
00710               normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
00711               if ( !BER_BVISNULL( &group_oc ) ) {
00712                      normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
00713                      if ( !BER_BVISNULL( &member_at ) ) {
00714                             normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
00715                      }
00716               }
00717 
00718               normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
00719               ptr = lutil_strcopy( normalized->bv_val, "group" );
00720               if ( !BER_BVISNULL( &group_oc ) ) {
00721                      ptr[ 0 ] = '/';
00722                      ptr++;
00723                      ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
00724                      if ( !BER_BVISNULL( &member_at ) ) {
00725                             ptr[ 0 ] = '/';
00726                             ptr++;
00727                             ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
00728                      }
00729               }
00730               ptr[ 0 ] = ':';
00731               ptr++;
00732               ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
00733               ptr[ 0 ] = '\0';
00734               ber_memfree_x( out.bv_val, ctx );
00735 
00736               return rc;
00737        }
00738 
00739        /*
00740         * ldap:///<base>??<scope>?<filter>
00741         * <scope> ::= {base|one|subtree}
00742         *
00743         * <scope> defaults to "base"
00744         * <base> must pass DN normalization
00745         * <filter> must pass str2filter()
00746         */
00747        rc = ldap_url_parse( val->bv_val, &ludp );
00748        switch ( rc ) {
00749        case LDAP_URL_SUCCESS:
00750               /* FIXME: the check is pedantic, but I think it's necessary,
00751                * because people tend to use things like ldaps:// which
00752                * gives the idea SSL is being used.  Maybe we could
00753                * accept ldapi:// as well, but the point is that we use
00754                * an URL as an easy means to define bits of a search with
00755                * little parsing.
00756                */
00757               if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
00758                      /*
00759                       * must be ldap:///
00760                       */
00761                      rc = LDAP_INVALID_SYNTAX;
00762                      goto done;
00763               }
00764 
00765               AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
00766               break;
00767 
00768        case LDAP_URL_ERR_BADSCHEME:
00769               /*
00770                * last chance: assume it's a(n exact) DN ...
00771                *
00772                * NOTE: must pass DN normalization
00773                */
00774               ldap_free_urldesc( ludp );
00775               bv.bv_val = val->bv_val;
00776               scope = LDAP_X_SCOPE_EXACT;
00777               goto is_dn;
00778 
00779        default:
00780               rc = LDAP_INVALID_SYNTAX;
00781               goto done;
00782        }
00783 
00784        if ( ( ludp->lud_host && *ludp->lud_host )
00785               || ludp->lud_attrs || ludp->lud_exts )
00786        {
00787               /* host part must be empty */
00788               /* attrs and extensions parts must be empty */
00789               rc = LDAP_INVALID_SYNTAX;
00790               goto done;
00791        }
00792 
00793        /* Grab the filter */
00794        if ( ludp->lud_filter ) {
00795               struct berval filterstr;
00796               Filter        *f;
00797 
00798               lud_filter = ludp->lud_filter;
00799 
00800               f = str2filter( lud_filter );
00801               if ( f == NULL ) {
00802                      rc = LDAP_INVALID_SYNTAX;
00803                      goto done;
00804               }
00805               filter2bv( f, &filterstr );
00806               filter_free( f );
00807               if ( BER_BVISNULL( &filterstr ) ) {
00808                      rc = LDAP_INVALID_SYNTAX;
00809                      goto done;
00810               }
00811 
00812               ludp->lud_filter = filterstr.bv_val;
00813        }
00814 
00815        /* Grab the searchbase */
00816        assert( ludp->lud_dn != NULL );
00817        if ( ludp->lud_dn ) {
00818               struct berval out = BER_BVNULL;
00819 
00820               lud_dn = ludp->lud_dn;
00821 
00822               ber_str2bv( lud_dn, 0, 0, &bv );
00823               if ( normalize ) {
00824                      rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
00825               } else {
00826                      rc = dnPretty( NULL, &bv, &out, ctx );
00827               }
00828 
00829               if ( rc != LDAP_SUCCESS ) {
00830                      goto done;
00831               }
00832 
00833               ludp->lud_dn = out.bv_val;
00834        }
00835 
00836        ludp->lud_port = 0;
00837        normalized->bv_val = ldap_url_desc2str( ludp );
00838        if ( normalized->bv_val ) {
00839               normalized->bv_len = strlen( normalized->bv_val );
00840 
00841        } else {
00842               rc = LDAP_INVALID_SYNTAX;
00843        }
00844 
00845 done:
00846        if ( lud_filter ) {
00847               if ( ludp->lud_filter != lud_filter ) {
00848                      ber_memfree( ludp->lud_filter );
00849               }
00850               ludp->lud_filter = lud_filter;
00851        }
00852 
00853        if ( lud_dn ) {
00854               if ( ludp->lud_dn != lud_dn ) {
00855                      ber_memfree( ludp->lud_dn );
00856               }
00857               ludp->lud_dn = lud_dn;
00858        }
00859 
00860        ldap_free_urldesc( ludp );
00861 
00862        return( rc );
00863 }
00864 
00865 int
00866 authzNormalize(
00867        slap_mask_t   usage,
00868        Syntax        *syntax,
00869        MatchingRule  *mr,
00870        struct berval *val,
00871        struct berval *normalized,
00872        void          *ctx )
00873 {
00874        int           rc;
00875 
00876        Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
00877               val->bv_val, 0, 0 );
00878 
00879        rc = authzPrettyNormal( val, normalized, ctx, 1 );
00880 
00881        Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
00882               normalized->bv_val, rc, 0 );
00883 
00884        return rc;
00885 }
00886 
00887 int
00888 authzPretty(
00889        Syntax *syntax,
00890        struct berval *val,
00891        struct berval *out,
00892        void *ctx)
00893 {
00894        int           rc;
00895 
00896        Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
00897               val->bv_val, 0, 0 );
00898 
00899        rc = authzPrettyNormal( val, out, ctx, 0 );
00900 
00901        Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
00902               out->bv_val, rc, 0 );
00903 
00904        return rc;
00905 }
00906 
00907 
00908 static int
00909 slap_parseURI(
00910        Operation     *op,
00911        struct berval *uri,
00912        struct berval *base,
00913        struct berval *nbase,
00914        int           *scope,
00915        Filter        **filter,
00916        struct berval *fstr,
00917        int           normalize )
00918 {
00919        struct berval bv;
00920        int           rc;
00921        LDAPURLDesc   *ludp;
00922 
00923        struct berval idx;
00924 
00925        assert( uri != NULL && !BER_BVISNULL( uri ) );
00926        BER_BVZERO( base );
00927        BER_BVZERO( nbase );
00928        BER_BVZERO( fstr );
00929        *scope = -1;
00930        *filter = NULL;
00931 
00932        Debug( LDAP_DEBUG_TRACE,
00933               "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
00934 
00935        rc = LDAP_PROTOCOL_ERROR;
00936 
00937        idx = *uri;
00938        if ( idx.bv_val[ 0 ] == '{' ) {
00939               char   *ptr;
00940 
00941               ptr = ber_bvchr( &idx, '}' ) + 1;
00942 
00943               assert( ptr != (void *)1 );
00944 
00945               idx.bv_len -= ptr - idx.bv_val;
00946               idx.bv_val = ptr;
00947               uri = &idx;
00948        }
00949 
00950        /*
00951         * dn[.<dnstyle>]:<dnpattern>
00952         * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
00953         *
00954         * <dnstyle> defaults to "exact"
00955         * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
00956         */
00957        if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
00958               bv.bv_val = uri->bv_val + STRLENOF( "dn" );
00959 
00960               if ( bv.bv_val[ 0 ] == '.' ) {
00961                      bv.bv_val++;
00962 
00963                      if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
00964                             bv.bv_val += STRLENOF( "exact:" );
00965                             *scope = LDAP_X_SCOPE_EXACT;
00966 
00967                      } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
00968                             bv.bv_val += STRLENOF( "regex:" );
00969                             *scope = LDAP_X_SCOPE_REGEX;
00970 
00971                      } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
00972                             bv.bv_val += STRLENOF( "children:" );
00973                             *scope = LDAP_X_SCOPE_CHILDREN;
00974 
00975                      } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
00976                             bv.bv_val += STRLENOF( "subtree:" );
00977                             *scope = LDAP_X_SCOPE_SUBTREE;
00978 
00979                      } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
00980                             bv.bv_val += STRLENOF( "onelevel:" );
00981                             *scope = LDAP_X_SCOPE_ONELEVEL;
00982 
00983                      } else {
00984                             return LDAP_PROTOCOL_ERROR;
00985                      }
00986 
00987               } else {
00988                      if ( bv.bv_val[ 0 ] != ':' ) {
00989                             return LDAP_PROTOCOL_ERROR;
00990                      }
00991                      *scope = LDAP_X_SCOPE_EXACT;
00992                      bv.bv_val++;
00993               }
00994 
00995               bv.bv_val += strspn( bv.bv_val, " " );
00996               /* jump here in case no type specification was present
00997                * and uri was not an URI... HEADS-UP: assuming EXACT */
00998 is_dn:        bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
00999 
01000               /* a single '*' means any DN without using regexes */
01001               if ( ber_bvccmp( &bv, '*' ) ) {
01002                      *scope = LDAP_X_SCOPE_USERS;
01003               }
01004 
01005               switch ( *scope ) {
01006               case LDAP_X_SCOPE_EXACT:
01007               case LDAP_X_SCOPE_CHILDREN:
01008               case LDAP_X_SCOPE_SUBTREE:
01009               case LDAP_X_SCOPE_ONELEVEL:
01010                      if ( normalize ) {
01011                             rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
01012                             if( rc != LDAP_SUCCESS ) {
01013                                    *scope = -1;
01014                             }
01015                      } else {
01016                             ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
01017                             rc = LDAP_SUCCESS;
01018                      }
01019                      break;
01020 
01021               case LDAP_X_SCOPE_REGEX:
01022                      ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
01023 
01024               case LDAP_X_SCOPE_USERS:
01025                      rc = LDAP_SUCCESS;
01026                      break;
01027 
01028               default:
01029                      *scope = -1;
01030                      break;
01031               }
01032 
01033               return rc;
01034 
01035        /*
01036         * u:<uid>
01037         */
01038        } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
01039                      && ( uri->bv_val[ 1 ] == ':' 
01040                             || uri->bv_val[ 1 ] == '/' 
01041                             || uri->bv_val[ 1 ] == '.' ) )
01042        {
01043               Connection    c = *op->o_conn;
01044               char          buf[ SLAP_LDAPDN_MAXLEN ];
01045               struct berval id,
01046                             user = BER_BVNULL,
01047                             realm = BER_BVNULL,
01048                             mech = BER_BVNULL;
01049 
01050               if ( sizeof( buf ) <= uri->bv_len ) {
01051                      return LDAP_INVALID_SYNTAX;
01052               }
01053 
01054               id.bv_len = uri->bv_len;
01055               id.bv_val = buf;
01056               strncpy( buf, uri->bv_val, sizeof( buf ) );
01057 
01058               rc = slap_parse_user( &id, &user, &realm, &mech );
01059               if ( rc != LDAP_SUCCESS ) {
01060                      return rc;
01061               }
01062 
01063               if ( !BER_BVISNULL( &mech ) ) {
01064                      c.c_sasl_bind_mech = mech;
01065               } else {
01066                      BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
01067               }
01068               
01069               rc = slap_sasl_getdn( &c, op, &user,
01070                             realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
01071 
01072               if ( rc == LDAP_SUCCESS ) {
01073                      *scope = LDAP_X_SCOPE_EXACT;
01074               }
01075 
01076               return rc;
01077 
01078        /*
01079         * group[/<groupoc>[/<groupat>]]:<groupdn>
01080         *
01081         * groupoc defaults to "groupOfNames"
01082         * groupat defaults to "member"
01083         * 
01084         * <groupdn> must pass DN normalization
01085         */
01086        } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
01087        {
01088               struct berval group_dn = BER_BVNULL,
01089                             group_oc = BER_BVNULL,
01090                             member_at = BER_BVNULL;
01091               char          *tmp;
01092 
01093               bv.bv_val = uri->bv_val + STRLENOF( "group" );
01094               bv.bv_len = uri->bv_len - STRLENOF( "group" );
01095               group_dn.bv_val = ber_bvchr( &bv, ':' );
01096               if ( group_dn.bv_val == NULL ) {
01097                      /* last chance: assume it's a(n exact) DN ... */
01098                      bv.bv_val = uri->bv_val;
01099                      *scope = LDAP_X_SCOPE_EXACT;
01100                      goto is_dn;
01101               }
01102               
01103               if ( bv.bv_val[ 0 ] == '/' ) {
01104                      group_oc.bv_val = &bv.bv_val[ 1 ];
01105                      group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
01106 
01107                      member_at.bv_val = ber_bvchr( &group_oc, '/' );
01108                      if ( member_at.bv_val ) {
01109                             group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
01110                             member_at.bv_val++;
01111                             member_at.bv_len = group_dn.bv_val - member_at.bv_val;
01112 
01113                      } else {
01114                             BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
01115                      }
01116 
01117               } else {
01118                      BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
01119                      BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
01120               }
01121               group_dn.bv_val++;
01122               group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
01123 
01124               if ( normalize ) {
01125                      rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
01126                      if ( rc != LDAP_SUCCESS ) {
01127                             *scope = -1;
01128                             return rc;
01129                      }
01130               } else {
01131                      ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
01132                      rc = LDAP_SUCCESS;
01133               }
01134               *scope = LDAP_X_SCOPE_GROUP;
01135 
01136               /* FIXME: caller needs to add value of member attribute
01137                * and close brackets twice */
01138               fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
01139                      + group_oc.bv_len + member_at.bv_len;
01140               fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
01141 
01142               tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
01143                             STRLENOF( "(&(objectClass=" /* )) */ ) );
01144               tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
01145               tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
01146                             STRLENOF( /* ( */ ")(" /* ) */ ) );
01147               tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
01148               tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
01149 
01150               return rc;
01151        }
01152 
01153        /*
01154         * ldap:///<base>??<scope>?<filter>
01155         * <scope> ::= {base|one|subtree}
01156         *
01157         * <scope> defaults to "base"
01158         * <base> must pass DN normalization
01159         * <filter> must pass str2filter()
01160         */
01161        rc = ldap_url_parse( uri->bv_val, &ludp );
01162        switch ( rc ) {
01163        case LDAP_URL_SUCCESS:
01164               /* FIXME: the check is pedantic, but I think it's necessary,
01165                * because people tend to use things like ldaps:// which
01166                * gives the idea SSL is being used.  Maybe we could
01167                * accept ldapi:// as well, but the point is that we use
01168                * an URL as an easy means to define bits of a search with
01169                * little parsing.
01170                */
01171               if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
01172                      /*
01173                       * must be ldap:///
01174                       */
01175                      rc = LDAP_PROTOCOL_ERROR;
01176                      goto done;
01177               }
01178               break;
01179 
01180        case LDAP_URL_ERR_BADSCHEME:
01181               /*
01182                * last chance: assume it's a(n exact) DN ...
01183                *
01184                * NOTE: must pass DN normalization
01185                */
01186               ldap_free_urldesc( ludp );
01187               bv.bv_val = uri->bv_val;
01188               *scope = LDAP_X_SCOPE_EXACT;
01189               goto is_dn;
01190 
01191        default:
01192               rc = LDAP_PROTOCOL_ERROR;
01193               goto done;
01194        }
01195 
01196        if ( ( ludp->lud_host && *ludp->lud_host )
01197               || ludp->lud_attrs || ludp->lud_exts )
01198        {
01199               /* host part must be empty */
01200               /* attrs and extensions parts must be empty */
01201               rc = LDAP_PROTOCOL_ERROR;
01202               goto done;
01203        }
01204 
01205        /* Grab the scope */
01206        *scope = ludp->lud_scope;
01207 
01208        /* Grab the filter */
01209        if ( ludp->lud_filter ) {
01210               *filter = str2filter_x( op, ludp->lud_filter );
01211               if ( *filter == NULL ) {
01212                      rc = LDAP_PROTOCOL_ERROR;
01213                      goto done;
01214               }
01215               ber_str2bv( ludp->lud_filter, 0, 0, fstr );
01216        }
01217 
01218        /* Grab the searchbase */
01219        ber_str2bv( ludp->lud_dn, 0, 0, base );
01220        if ( normalize ) {
01221               rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
01222        } else {
01223               ber_dupbv_x( nbase, base, op->o_tmpmemctx );
01224               rc = LDAP_SUCCESS;
01225        }
01226 
01227 done:
01228        if( rc != LDAP_SUCCESS ) {
01229               if( *filter ) filter_free_x( op, *filter, 1 );
01230               BER_BVZERO( base );
01231               BER_BVZERO( fstr );
01232        } else {
01233               /* Don't free these, return them to caller */
01234               ludp->lud_filter = NULL;
01235               ludp->lud_dn = NULL;
01236        }
01237 
01238        ldap_free_urldesc( ludp );
01239        return( rc );
01240 }
01241 
01242 #ifndef SLAP_AUTH_REWRITE
01243 static int slap_sasl_rx_off(char *rep, int *off)
01244 {
01245        const char *c;
01246        int n;
01247 
01248        /* Precompile replace pattern. Find the $<n> placeholders */
01249        off[0] = -2;
01250        n = 1;
01251        for ( c = rep;        *c;  c++ ) {
01252               if ( *c == '\\' && c[1] ) {
01253                      c++;
01254                      continue;
01255               }
01256               if ( *c == '$' ) {
01257                      if ( n == SASLREGEX_REPLACE ) {
01258                             Debug( LDAP_DEBUG_ANY,
01259                                    "SASL replace pattern %s has too many $n "
01260                                           "placeholders (max %d)\n",
01261                                    rep, SASLREGEX_REPLACE, 0 );
01262 
01263                             return( LDAP_OTHER );
01264                      }
01265                      off[n] = c - rep;
01266                      n++;
01267               }
01268        }
01269 
01270        /* Final placeholder, after the last $n */
01271        off[n] = c - rep;
01272        n++;
01273        off[n] = -1;
01274        return( LDAP_SUCCESS );
01275 }
01276 #endif /* ! SLAP_AUTH_REWRITE */
01277 
01278 #ifdef SLAP_AUTH_REWRITE
01279 int slap_sasl_rewrite_config( 
01280               const char    *fname,
01281               int           lineno,
01282               int           argc,
01283               char          **argv
01284 )
01285 {
01286        int    rc;
01287        char   *savearg0;
01288 
01289        /* init at first call */
01290        if ( sasl_rwinfo == NULL ) {
01291               sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
01292        }
01293 
01294        /* strip "authid-" prefix for parsing */
01295        savearg0 = argv[0];
01296        argv[0] += STRLENOF( "authid-" );
01297        rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
01298        argv[0] = savearg0;
01299 
01300        return rc;
01301 }
01302 
01303 static int
01304 slap_sasl_rewrite_destroy( void )
01305 {
01306        if ( sasl_rwinfo ) {
01307               rewrite_info_delete( &sasl_rwinfo );
01308               sasl_rwinfo = NULL;
01309        }
01310 
01311        return 0;
01312 }
01313 
01314 int slap_sasl_regexp_rewrite_config(
01315               const char    *fname,
01316               int           lineno,
01317               const char    *match,
01318               const char    *replace,
01319               const char    *context )
01320 {
01321        int    rc;
01322        char   *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
01323 
01324        /* init at first call */
01325        if ( sasl_rwinfo == NULL ) {
01326               char *argvEngine[] = { "rewriteEngine", "on", NULL };
01327               char *argvContext[] = { "rewriteContext", NULL, NULL };
01328 
01329               /* initialize rewrite engine */
01330               sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
01331 
01332               /* switch on rewrite engine */
01333               rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
01334               if (rc != LDAP_SUCCESS) {
01335                      return rc;
01336               }
01337 
01338               /* create generic authid context */
01339               argvContext[1] = AUTHID_CONTEXT;
01340               rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
01341               if (rc != LDAP_SUCCESS) {
01342                      return rc;
01343               }
01344        }
01345 
01346        argvRule[1] = (char *)match;
01347        argvRule[2] = (char *)replace;
01348        rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
01349 
01350        return rc;
01351 }
01352 #endif /* SLAP_AUTH_REWRITE */
01353 
01354 int slap_sasl_regexp_config( const char *match, const char *replace )
01355 {
01356        int rc;
01357        SaslRegexp_t *reg;
01358 
01359        SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
01360          (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
01361 
01362        reg = &SaslRegexp[nSaslRegexp];
01363 
01364 #ifdef SLAP_AUTH_REWRITE
01365        rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
01366                      match, replace, AUTHID_CONTEXT );
01367 #else /* ! SLAP_AUTH_REWRITE */
01368 
01369        /* Precompile matching pattern */
01370        rc = regcomp( &reg->sr_workspace, match, REG_EXTENDED|REG_ICASE );
01371        if ( rc ) {
01372               Debug( LDAP_DEBUG_ANY,
01373                      "SASL match pattern %s could not be compiled by regexp engine\n",
01374                      match, 0, 0 );
01375 
01376 #ifdef ENABLE_REWRITE
01377               /* Dummy block to force symbol references in librewrite */
01378               if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
01379                      rewrite_info_init( 0 );
01380               }
01381 #endif
01382               return( LDAP_OTHER );
01383        }
01384 
01385        rc = slap_sasl_rx_off( replace, reg->sr_offset );
01386 #endif /* ! SLAP_AUTH_REWRITE */
01387        if ( rc == LDAP_SUCCESS ) {
01388               reg->sr_match = ch_strdup( match );
01389               reg->sr_replace = ch_strdup( replace );
01390 
01391               nSaslRegexp++;
01392        }
01393 
01394        return rc;
01395 }
01396 
01397 void
01398 slap_sasl_regexp_destroy( void )
01399 {
01400        if ( SaslRegexp ) {
01401               int    n;
01402 
01403               for ( n = 0; n < nSaslRegexp; n++ ) {
01404                      ch_free( SaslRegexp[ n ].sr_match );
01405                      ch_free( SaslRegexp[ n ].sr_replace );
01406 #ifndef SLAP_AUTH_REWRITE
01407                      regfree( &SaslRegexp[ n ].sr_workspace );
01408 #endif /* SLAP_AUTH_REWRITE */
01409               }
01410 
01411               ch_free( SaslRegexp );
01412        }
01413 
01414 #ifdef SLAP_AUTH_REWRITE
01415        slap_sasl_rewrite_destroy();
01416 #endif /* SLAP_AUTH_REWRITE */
01417 }
01418 
01419 void slap_sasl_regexp_unparse( BerVarray *out )
01420 {
01421        int i;
01422        BerVarray bva = NULL;
01423        char ibuf[32], *ptr;
01424        struct berval idx;
01425 
01426        if ( !nSaslRegexp ) return;
01427 
01428        idx.bv_val = ibuf;
01429        bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
01430        BER_BVZERO(bva+nSaslRegexp);
01431        for ( i=0; i<nSaslRegexp; i++ ) {
01432               idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
01433               bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
01434                      strlen( SaslRegexp[i].sr_replace ) + 5;
01435               bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
01436               ptr = lutil_strcopy( bva[i].bv_val, ibuf );
01437               *ptr++ = '"';
01438               ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
01439               ptr = lutil_strcopy( ptr, "\" \"" );
01440               ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
01441               *ptr++ = '"';
01442               *ptr = '\0';
01443        }
01444        *out = bva;
01445 }
01446 
01447 #ifndef SLAP_AUTH_REWRITE
01448 /* Perform replacement on regexp matches */
01449 static void slap_sasl_rx_exp(
01450        const char *rep,
01451        const int *off,
01452        regmatch_t *str,
01453        const char *saslname,
01454        struct berval *out,
01455        void *ctx )
01456 {
01457        int i, n, len, insert;
01458 
01459        /* Get the total length of the final URI */
01460 
01461        n=1;
01462        len = 0;
01463        while( off[n] >= 0 ) {
01464               /* Len of next section from replacement string (x,y,z above) */
01465               len += off[n] - off[n-1] - 2;
01466               if( off[n+1] < 0)
01467                      break;
01468 
01469               /* Len of string from saslname that matched next $i  (b,d above) */
01470               i = rep[ off[n] + 1 ]       - '0';
01471               len += str[i].rm_eo - str[i].rm_so;
01472               n++;
01473        }
01474        out->bv_val = slap_sl_malloc( len + 1, ctx );
01475        out->bv_len = len;
01476 
01477        /* Fill in URI with replace string, replacing $i as we go */
01478        n=1;
01479        insert = 0;
01480        while( off[n] >= 0) {
01481               /* Paste in next section from replacement string (x,y,z above) */
01482               len = off[n] - off[n-1] - 2;
01483               strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
01484               insert += len;
01485               if( off[n+1] < 0)
01486                      break;
01487 
01488               /* Paste in string from saslname that matched next $i  (b,d above) */
01489               i = rep[ off[n] + 1 ]       - '0';
01490               len = str[i].rm_eo - str[i].rm_so;
01491               strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
01492               insert += len;
01493 
01494               n++;
01495        }
01496 
01497        out->bv_val[insert] = '\0';
01498 }
01499 #endif /* ! SLAP_AUTH_REWRITE */
01500 
01501 /* Take the passed in SASL name and attempt to convert it into an
01502    LDAP URI to find the matching LDAP entry, using the pattern matching
01503    strings given in the saslregexp config file directive(s) */
01504 
01505 static int slap_authz_regexp( struct berval *in, struct berval *out,
01506               int flags, void *ctx )
01507 {
01508 #ifdef SLAP_AUTH_REWRITE
01509        const char    *context = AUTHID_CONTEXT;
01510 
01511        if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
01512               return 0;
01513        }
01514 
01515        /* FIXME: if aware of authc/authz mapping, 
01516         * we could use different contexts ... */
01517        switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 
01518                             &out->bv_val ) )
01519        {
01520        case REWRITE_REGEXEC_OK:
01521               if ( !BER_BVISNULL( out ) ) {
01522                      char *val = out->bv_val;
01523                      ber_str2bv_x( val, 0, 1, out, ctx );
01524                      if ( val != in->bv_val ) {
01525                             free( val );
01526                      }
01527               } else {
01528                      ber_dupbv_x( out, in, ctx );
01529               }
01530               Debug( LDAP_DEBUG_ARGS,
01531                      "[rw] %s: \"%s\" -> \"%s\"\n",
01532                      context, in->bv_val, out->bv_val );              
01533               return 1;
01534               
01535        case REWRITE_REGEXEC_UNWILLING:
01536        case REWRITE_REGEXEC_ERR:
01537        default:
01538               return 0;
01539        }
01540 
01541 #else /* ! SLAP_AUTH_REWRITE */
01542        char *saslname = in->bv_val;
01543        SaslRegexp_t *reg;
01544        regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
01545        int i;
01546 
01547        memset( out, 0, sizeof( *out ) );
01548 
01549        Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
01550           saslname, 0, 0 );
01551 
01552        if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
01553               return( 0 );
01554        }
01555 
01556        /* Match the normalized SASL name to the saslregexp patterns */
01557        for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
01558               if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
01559                 sr_strings, 0)  == 0 )
01560                      break;
01561        }
01562 
01563        if( i >= nSaslRegexp ) return( 0 );
01564 
01565        /*
01566         * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
01567         * replace pattern of the form "x$1y$2z". The returned string needs
01568         * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
01569         */
01570        slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
01571               sr_strings, saslname, out, ctx );
01572 
01573        Debug( LDAP_DEBUG_TRACE,
01574               "slap_authz_regexp: converted SASL name to %s\n",
01575               BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
01576 
01577        return( 1 );
01578 #endif /* ! SLAP_AUTH_REWRITE */
01579 }
01580 
01581 /* This callback actually does some work...*/
01582 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
01583 {
01584        struct berval *ndn = op->o_callback->sc_private;
01585 
01586        if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
01587 
01588        /* We only want to be called once */
01589        if ( !BER_BVISNULL( ndn ) ) {
01590               op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
01591               BER_BVZERO( ndn );
01592 
01593               Debug( LDAP_DEBUG_TRACE,
01594                      "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
01595                      op->o_log_prefix, 0, 0 );
01596               return LDAP_UNAVAILABLE; /* short-circuit the search */
01597        }
01598 
01599        ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
01600        return LDAP_SUCCESS;
01601 }
01602 
01603 
01604 typedef struct smatch_info {
01605        struct berval *dn;
01606        int match;
01607 } smatch_info;
01608 
01609 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
01610 {
01611        smatch_info *sm = o->o_callback->sc_private;
01612 
01613        if (rs->sr_type != REP_SEARCH) return 0;
01614 
01615        if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
01616               sm->match = 1;
01617               return LDAP_UNAVAILABLE;    /* short-circuit the search */
01618        }
01619 
01620        return 0;
01621 }
01622 
01623 int
01624 slap_sasl_matches( Operation *op, BerVarray rules,
01625               struct berval *assertDN, struct berval *authc )
01626 {
01627        int    rc = LDAP_INAPPROPRIATE_AUTH;
01628 
01629        if ( rules != NULL ) {
01630               int    i;
01631 
01632               for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
01633                      rc = slap_sasl_match( op, &rules[i], assertDN, authc );
01634                      if ( rc == LDAP_SUCCESS ) break;
01635               }
01636        }
01637        
01638        return rc;
01639 }
01640 
01641 /*
01642  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
01643  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
01644  * the rule must be used as an internal search for entries. If that search
01645  * returns the *assertDN entry, the match is successful.
01646  *
01647  * The assertDN should not have the dn: prefix
01648  */
01649 
01650 static int
01651 slap_sasl_match( Operation *opx, struct berval *rule,
01652        struct berval *assertDN, struct berval *authc )
01653 {
01654        int rc; 
01655        regex_t reg;
01656        smatch_info sm;
01657        slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
01658        Operation op = {0};
01659        SlapReply rs = {REP_RESULT};
01660        struct berval base = BER_BVNULL;
01661 
01662        sm.dn = assertDN;
01663        sm.match = 0;
01664        cb.sc_private = &sm;
01665 
01666        Debug( LDAP_DEBUG_TRACE,
01667           "===>slap_sasl_match: comparing DN %s to rule %s\n",
01668               assertDN->bv_len ? assertDN->bv_val : "(null)", rule->bv_val, 0 );
01669 
01670        /* NOTE: don't normalize rule if authz syntax is enabled */
01671        rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
01672               &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
01673 
01674        if( rc != LDAP_SUCCESS ) goto CONCLUDED;
01675 
01676        switch ( op.ors_scope ) {
01677        case LDAP_X_SCOPE_EXACT:
01678 exact_match:
01679               if ( dn_match( &op.o_req_ndn, assertDN ) ) {
01680                      rc = LDAP_SUCCESS;
01681               } else {
01682                      rc = LDAP_INAPPROPRIATE_AUTH;
01683               }
01684               goto CONCLUDED;
01685 
01686        case LDAP_X_SCOPE_CHILDREN:
01687        case LDAP_X_SCOPE_SUBTREE:
01688        case LDAP_X_SCOPE_ONELEVEL:
01689        {
01690               int    d = assertDN->bv_len - op.o_req_ndn.bv_len;
01691 
01692               rc = LDAP_INAPPROPRIATE_AUTH;
01693 
01694               if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
01695                      goto exact_match;
01696 
01697               } else if ( d > 0 ) {
01698                      struct berval bv;
01699 
01700                      /* leave room for at least one char of attributeType,
01701                       * one for '=' and one for ',' */
01702                      if ( d < (int) STRLENOF( "x=,") ) {
01703                             goto CONCLUDED;
01704                      }
01705 
01706                      bv.bv_len = op.o_req_ndn.bv_len;
01707                      bv.bv_val = assertDN->bv_val + d;
01708 
01709                      if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
01710                             switch ( op.ors_scope ) {
01711                             case LDAP_X_SCOPE_SUBTREE:
01712                             case LDAP_X_SCOPE_CHILDREN:
01713                                    rc = LDAP_SUCCESS;
01714                                    break;
01715 
01716                             case LDAP_X_SCOPE_ONELEVEL:
01717                             {
01718                                    struct berval pdn;
01719 
01720                                    dnParent( assertDN, &pdn );
01721                                    /* the common portion of the DN
01722                                     * already matches, so only check
01723                                     * if parent DN of assertedDN 
01724                                     * is all the pattern */
01725                                    if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
01726                                           rc = LDAP_SUCCESS;
01727                                    }
01728                                    break;
01729                             }
01730                             default:
01731                                    /* at present, impossible */
01732                                    assert( 0 );
01733                             }
01734                      }
01735               }
01736               goto CONCLUDED;
01737        }
01738 
01739        case LDAP_X_SCOPE_REGEX:
01740               rc = regcomp(&reg, op.o_req_ndn.bv_val,
01741                      REG_EXTENDED|REG_ICASE|REG_NOSUB);
01742               if ( rc == 0 ) {
01743                      rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
01744                      regfree( &reg );
01745               }
01746               if ( rc == 0 ) {
01747                      rc = LDAP_SUCCESS;
01748               } else {
01749                      rc = LDAP_INAPPROPRIATE_AUTH;
01750               }
01751               goto CONCLUDED;
01752 
01753        case LDAP_X_SCOPE_GROUP: {
01754               char   *tmp;
01755 
01756               /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
01757                * we need to append the <assertDN> so that the <group_dn> is searched
01758                * with scope "base", and the filter ensures that <assertDN> is
01759                * member of the group */
01760               tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
01761                      assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
01762               if ( tmp == NULL ) {
01763                      rc = LDAP_NO_MEMORY;
01764                      goto CONCLUDED;
01765               }
01766               op.ors_filterstr.bv_val = tmp;
01767               
01768               tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
01769               tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
01770 
01771               /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
01772               op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
01773               if ( op.ors_filter == NULL ) {
01774                      rc = LDAP_PROTOCOL_ERROR;
01775                      goto CONCLUDED;
01776               }
01777               op.ors_scope = LDAP_SCOPE_BASE;
01778 
01779               /* hijack match DN: use that of the group instead of the assertDN;
01780                * assertDN is now in the filter */
01781               sm.dn = &op.o_req_ndn;
01782 
01783               /* do the search */
01784               break;
01785               }
01786 
01787        case LDAP_X_SCOPE_USERS:
01788               if ( !BER_BVISEMPTY( assertDN ) ) {
01789                      rc = LDAP_SUCCESS;
01790               } else {
01791                      rc = LDAP_INAPPROPRIATE_AUTH;
01792               }
01793               goto CONCLUDED;
01794 
01795        default:
01796               break;
01797        }
01798 
01799        /* Must run an internal search. */
01800        if ( op.ors_filter == NULL ) {
01801               rc = LDAP_FILTER_ERROR;
01802               goto CONCLUDED;
01803        }
01804 
01805        Debug( LDAP_DEBUG_TRACE,
01806           "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
01807           op.o_req_ndn.bv_val, op.ors_scope, 0 );
01808 
01809        op.o_bd = select_backend( &op.o_req_ndn, 1 );
01810        if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
01811               rc = LDAP_INAPPROPRIATE_AUTH;
01812               goto CONCLUDED;
01813        }
01814 
01815        op.o_hdr = opx->o_hdr;
01816        op.o_tag = LDAP_REQ_SEARCH;
01817        op.o_ndn = *authc;
01818        op.o_callback = &cb;
01819        slap_op_time( &op.o_time, &op.o_tincr );
01820        op.o_do_not_cache = 1;
01821        op.o_is_auth_check = 1;
01822        /* use req_ndn as req_dn instead of non-pretty base of uri */
01823        if( !BER_BVISNULL( &base ) ) {
01824               ch_free( base.bv_val );
01825               /* just in case... */
01826               BER_BVZERO( &base );
01827        }
01828        ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
01829        op.ors_deref = LDAP_DEREF_NEVER;
01830        op.ors_slimit = 1;
01831        op.ors_tlimit = SLAP_NO_LIMIT;
01832        op.ors_attrs = slap_anlist_no_attrs;
01833        op.ors_attrsonly = 1;
01834 
01835        op.o_bd->be_search( &op, &rs );
01836 
01837        if (sm.match) {
01838               rc = LDAP_SUCCESS;
01839        } else {
01840               rc = LDAP_INAPPROPRIATE_AUTH;
01841        }
01842 
01843 CONCLUDED:
01844        if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
01845        if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
01846        if( op.ors_filter ) filter_free_x( opx, op.ors_filter, 1 );
01847        if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
01848 
01849        Debug( LDAP_DEBUG_TRACE,
01850           "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
01851 
01852        return( rc );
01853 }
01854 
01855 
01856 /*
01857  * This function answers the question, "Can this ID authorize to that ID?",
01858  * based on authorization rules. The rules are stored in the *searchDN, in the
01859  * attribute named by *attr. If any of those rules map to the *assertDN, the
01860  * authorization is approved.
01861  *
01862  * The DNs should not have the dn: prefix
01863  */
01864 static int
01865 slap_sasl_check_authz( Operation *op,
01866        struct berval *searchDN,
01867        struct berval *assertDN,
01868        AttributeDescription *ad,
01869        struct berval *authc )
01870 {
01871        int           rc,
01872                      do_not_cache = op->o_do_not_cache;
01873        BerVarray     vals = NULL;
01874 
01875        Debug( LDAP_DEBUG_TRACE,
01876           "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
01877           assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
01878 
01879        /* ITS#4760: don't cache group access */
01880        op->o_do_not_cache = 1;
01881        rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
01882        op->o_do_not_cache = do_not_cache;
01883        if( rc != LDAP_SUCCESS ) goto COMPLETE;
01884 
01885        /* Check if the *assertDN matches any *vals */
01886        rc = slap_sasl_matches( op, vals, assertDN, authc );
01887 
01888 COMPLETE:
01889        if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
01890 
01891        Debug( LDAP_DEBUG_TRACE,
01892           "<==slap_sasl_check_authz: %s check returning %d\n",
01893               ad->ad_cname.bv_val, rc, 0);
01894 
01895        return( rc );
01896 }
01897 
01898 /*
01899  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
01900  * return the LDAP DN to which it matches. The SASL regexp rules in the config
01901  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
01902  * search with scope=base), just return the URI (or its searchbase). Otherwise
01903  * an internal search must be done, and if that search returns exactly one
01904  * entry, return the DN of that one entry.
01905  */
01906 void
01907 slap_sasl2dn(
01908        Operation     *opx,
01909        struct berval *saslname,
01910        struct berval *sasldn,
01911        int           flags )
01912 {
01913        int rc;
01914        slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
01915        Operation op = {0};
01916        SlapReply rs = {REP_RESULT};
01917        struct berval regout = BER_BVNULL;
01918        struct berval base = BER_BVNULL;
01919 
01920        Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
01921               "converting SASL name %s to a DN\n",
01922               saslname->bv_val, 0,0 );
01923 
01924        BER_BVZERO( sasldn );
01925        cb.sc_private = sasldn;
01926 
01927        /* Convert the SASL name into a minimal URI */
01928        if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
01929               goto FINISHED;
01930        }
01931 
01932        /* NOTE: always normalize regout because it results
01933         * from string submatch expansion */
01934        rc = slap_parseURI( opx, &regout, &base, &op.o_req_ndn,
01935               &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
01936        if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
01937        if ( rc != LDAP_SUCCESS ) {
01938               goto FINISHED;
01939        }
01940 
01941        /* Must do an internal search */
01942        op.o_bd = select_backend( &op.o_req_ndn, 1 );
01943 
01944        switch ( op.ors_scope ) {
01945        case LDAP_X_SCOPE_EXACT:
01946               *sasldn = op.o_req_ndn;
01947               BER_BVZERO( &op.o_req_ndn );
01948               /* intentionally continue to next case */
01949 
01950        case LDAP_X_SCOPE_REGEX:
01951        case LDAP_X_SCOPE_SUBTREE:
01952        case LDAP_X_SCOPE_CHILDREN:
01953        case LDAP_X_SCOPE_ONELEVEL:
01954        case LDAP_X_SCOPE_GROUP:
01955        case LDAP_X_SCOPE_USERS:
01956               /* correctly parsed, but illegal */
01957               goto FINISHED;
01958 
01959        case LDAP_SCOPE_BASE:
01960        case LDAP_SCOPE_ONELEVEL:
01961        case LDAP_SCOPE_SUBTREE:
01962        case LDAP_SCOPE_SUBORDINATE:
01963               /* do a search */
01964               break;
01965 
01966        default:
01967               /* catch unhandled cases (there shouldn't be) */
01968               assert( 0 );
01969        }
01970 
01971        Debug( LDAP_DEBUG_TRACE,
01972               "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
01973               op.o_req_ndn.bv_val, op.ors_scope, 0 );
01974 
01975        if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
01976               goto FINISHED;
01977        }
01978 
01979        /* Must run an internal search. */
01980        if ( op.ors_filter == NULL ) {
01981               rc = LDAP_FILTER_ERROR;
01982               goto FINISHED;
01983        }
01984 
01985        op.o_hdr = opx->o_hdr;
01986        op.o_tag = LDAP_REQ_SEARCH;
01987        op.o_ndn = opx->o_conn->c_ndn;
01988        op.o_callback = &cb;
01989        slap_op_time( &op.o_time, &op.o_tincr );
01990        op.o_do_not_cache = 1;
01991        op.o_is_auth_check = 1;
01992        op.ors_deref = LDAP_DEREF_NEVER;
01993        op.ors_slimit = 1;
01994        op.ors_tlimit = SLAP_NO_LIMIT;
01995        op.ors_attrs = slap_anlist_no_attrs;
01996        op.ors_attrsonly = 1;
01997        /* use req_ndn as req_dn instead of non-pretty base of uri */
01998        if( !BER_BVISNULL( &base ) ) {
01999               ch_free( base.bv_val );
02000               /* just in case... */
02001               BER_BVZERO( &base );
02002        }
02003        ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
02004 
02005        op.o_bd->be_search( &op, &rs );
02006        
02007 FINISHED:
02008        if( opx == opx->o_conn->c_sasl_bindop && !BER_BVISEMPTY( sasldn ) ) {
02009               opx->o_conn->c_authz_backend = op.o_bd;
02010        }
02011        if( !BER_BVISNULL( &op.o_req_dn ) ) {
02012               slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
02013        }
02014        if( !BER_BVISNULL( &op.o_req_ndn ) ) {
02015               slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
02016        }
02017        if( op.ors_filter ) {
02018               filter_free_x( opx, op.ors_filter, 1 );
02019        }
02020        if( !BER_BVISNULL( &op.ors_filterstr ) ) {
02021               ch_free( op.ors_filterstr.bv_val );
02022        }
02023 
02024        Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
02025               !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
02026 
02027        return;
02028 }
02029 
02030 
02031 /* Check if a bind can SASL authorize to another identity.
02032  * The DNs should not have the dn: prefix
02033  */
02034 
02035 int slap_sasl_authorized( Operation *op,
02036        struct berval *authcDN, struct berval *authzDN )
02037 {
02038        int rc = LDAP_INAPPROPRIATE_AUTH;
02039 
02040        /* User binding as anonymous */
02041        if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) {
02042               rc = LDAP_SUCCESS;
02043               goto DONE;
02044        }
02045 
02046        /* User is anonymous */
02047        if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) {
02048               goto DONE;
02049        }
02050 
02051        Debug( LDAP_DEBUG_TRACE,
02052           "==>slap_sasl_authorized: can %s become %s?\n",
02053               authcDN->bv_len ? authcDN->bv_val : "(null)",
02054               authzDN->bv_len ? authzDN->bv_val : "(null)",  0 );
02055 
02056        /* If person is authorizing to self, succeed */
02057        if ( dn_match( authcDN, authzDN ) ) {
02058               rc = LDAP_SUCCESS;
02059               goto DONE;
02060        }
02061 
02062        /* Allow the manager to authorize as any DN. */
02063        if( op->o_conn->c_authz_backend &&
02064               be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
02065        {
02066               rc = LDAP_SUCCESS;
02067               goto DONE;
02068        }
02069 
02070        /* Check source rules */
02071        if( authz_policy & SASL_AUTHZ_TO ) {
02072               rc = slap_sasl_check_authz( op, authcDN, authzDN,
02073                      slap_schema.si_ad_saslAuthzTo, authcDN );
02074               if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
02075                      goto DONE;
02076               }
02077        }
02078 
02079        /* Check destination rules */
02080        if( authz_policy & SASL_AUTHZ_FROM ) {
02081               rc = slap_sasl_check_authz( op, authzDN, authcDN,
02082                      slap_schema.si_ad_saslAuthzFrom, authcDN );
02083               if( rc == LDAP_SUCCESS ) {
02084                      goto DONE;
02085               }
02086        }
02087 
02088        rc = LDAP_INAPPROPRIATE_AUTH;
02089 
02090 DONE:
02091 
02092        Debug( LDAP_DEBUG_TRACE,
02093               "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
02094 
02095        return( rc );
02096 }