]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
import fix to ITS#4860
[openldap] / servers / slapd / saslauthz.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2007 The OpenLDAP Foundation.
5  * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #ifdef HAVE_LIMITS_H
21 #include <limits.h>
22 #endif
23
24 #include <ac/stdlib.h>
25 #include <ac/string.h>
26 #include <ac/ctype.h>
27
28 #include "slap.h"
29
30 #include "lutil.h"
31
32 #define SASLREGEX_REPLACE 10
33
34 #define LDAP_X_SCOPE_EXACT      ((ber_int_t) 0x0010)
35 #define LDAP_X_SCOPE_REGEX      ((ber_int_t) 0x0020)
36 #define LDAP_X_SCOPE_CHILDREN   ((ber_int_t) 0x0030)
37 #define LDAP_X_SCOPE_SUBTREE    ((ber_int_t) 0x0040)
38 #define LDAP_X_SCOPE_ONELEVEL   ((ber_int_t) 0x0050)
39 #define LDAP_X_SCOPE_GROUP      ((ber_int_t) 0x0060)
40 #define LDAP_X_SCOPE_USERS      ((ber_int_t) 0x0070)
41
42 /*
43  * IDs in DNauthzid form can now have a type specifier, that
44  * influences how they are used in related operations.
45  *
46  * syntax: dn[.{exact|regex}]:<val>
47  *
48  * dn.exact:    the value must pass normalization and is used 
49  *              in exact DN match.
50  * dn.regex:    the value is treated as a regular expression 
51  *              in matching DN values in authz{To|From}
52  *              attributes.
53  * dn:          for backwards compatibility reasons, the value 
54  *              is treated as a regular expression, and thus 
55  *              it is not normalized nor validated; it is used
56  *              in exact or regex comparisons based on the 
57  *              context.
58  *
59  * IDs in DNauthzid form can now have a type specifier, that
60  * influences how they are used in related operations.
61  *
62  * syntax: u[.mech[/realm]]:<val>
63  * 
64  * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
65  * and realm is mechanism specific realm (separate to those
66  * which are representable as part of the principal).
67  */
68
69 typedef struct sasl_regexp {
70   char *sr_match;                                               /* regexp match pattern */
71   char *sr_replace;                                     /* regexp replace pattern */
72   regex_t sr_workspace;                                 /* workspace for regexp engine */
73   int sr_offset[SASLREGEX_REPLACE+2];   /* offsets of $1,$2... in *replace */
74 } SaslRegexp_t;
75
76 static int nSaslRegexp = 0;
77 static SaslRegexp_t *SaslRegexp = NULL;
78
79 #ifdef SLAP_AUTH_REWRITE
80 #include "rewrite.h"
81 struct rewrite_info     *sasl_rwinfo = NULL;
82 #define AUTHID_CONTEXT  "authid"
83 #endif /* SLAP_AUTH_REWRITE */
84
85 /* What SASL proxy authorization policies are allowed? */
86 #define SASL_AUTHZ_NONE 0x00
87 #define SASL_AUTHZ_FROM 0x01
88 #define SASL_AUTHZ_TO   0x02
89 #define SASL_AUTHZ_AND  0x10
90
91 static const char *policy_txt[] = {
92         "none", "from", "to", "any"
93 };
94
95 static int authz_policy = SASL_AUTHZ_NONE;
96
97 static int
98 slap_sasl_match( Operation *opx, struct berval *rule,
99         struct berval *assertDN, struct berval *authc );
100
101 int slap_sasl_setpolicy( const char *arg )
102 {
103         int rc = LDAP_SUCCESS;
104
105         if ( strcasecmp( arg, "none" ) == 0 ) {
106                 authz_policy = SASL_AUTHZ_NONE;
107         } else if ( strcasecmp( arg, "from" ) == 0 ) {
108                 authz_policy = SASL_AUTHZ_FROM;
109         } else if ( strcasecmp( arg, "to" ) == 0 ) {
110                 authz_policy = SASL_AUTHZ_TO;
111         } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
112                 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
113         } else if ( strcasecmp( arg, "all" ) == 0 ) {
114                 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
115         } else {
116                 rc = LDAP_OTHER;
117         }
118         return rc;
119 }
120
121 const char * slap_sasl_getpolicy()
122 {
123         if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
124                 return "all";
125         else
126                 return policy_txt[authz_policy];
127 }
128
129 int slap_parse_user( struct berval *id, struct berval *user,
130                 struct berval *realm, struct berval *mech )
131 {
132         char    u;
133         
134         assert( id != NULL );
135         assert( !BER_BVISNULL( id ) );
136         assert( user != NULL );
137         assert( realm != NULL );
138         assert( mech != NULL );
139
140         u = id->bv_val[ 0 ];
141         
142         if ( u != 'u' && u != 'U' ) {
143                 /* called with something other than u: */
144                 return LDAP_PROTOCOL_ERROR;
145         }
146
147         /* uauthzid form:
148          *              u[.mech[/realm]]:user
149          */
150         
151         user->bv_val = ber_bvchr( id, ':' );
152         if ( BER_BVISNULL( user ) ) {
153                 return LDAP_PROTOCOL_ERROR;
154         }
155         user->bv_val[ 0 ] = '\0';
156         user->bv_val++;
157         user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
158
159         mech->bv_val = ber_bvchr( id, '.' );
160         if ( !BER_BVISNULL( mech ) ) {
161                 mech->bv_val[ 0 ] = '\0';
162                 mech->bv_val++;
163                 mech->bv_len = user->bv_val - mech->bv_val - 1;
164
165                 realm->bv_val = ber_bvchr( mech, '/' );
166
167                 if ( !BER_BVISNULL( realm ) ) {
168                         realm->bv_val[ 0 ] = '\0';
169                         realm->bv_val++;
170                         mech->bv_len = realm->bv_val - mech->bv_val - 1;
171                         realm->bv_len = user->bv_val - realm->bv_val - 1;
172                 }
173
174         } else {
175                 BER_BVZERO( realm );
176         }
177
178         if ( id->bv_val[ 1 ] != '\0' ) {
179                 return LDAP_PROTOCOL_ERROR;
180         }
181
182         if ( !BER_BVISNULL( mech ) ) {
183                 assert( mech->bv_val == id->bv_val + 2 );
184
185                 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
186                 mech->bv_val -= 2;
187         }
188
189         if ( !BER_BVISNULL( realm ) ) {
190                 assert( realm->bv_val >= id->bv_val + 2 );
191
192                 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
193                 realm->bv_val -= 2;
194         }
195
196         /* leave "u:" before user */
197         user->bv_val -= 2;
198         user->bv_len += 2;
199         user->bv_val[ 0 ] = u;
200         user->bv_val[ 1 ] = ':';
201
202         return LDAP_SUCCESS;
203 }
204
205 #ifdef SLAP_AUTHZ_SYNTAX
206 int
207 authzValidate(
208         Syntax *syntax,
209         struct berval *in )
210 {
211         struct berval   bv;
212         int             rc = LDAP_INVALID_SYNTAX;
213         LDAPURLDesc     *ludp = NULL;
214         int             scope = -1;
215
216         /*
217          * 1) <DN>
218          * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
219          * 3) dn.regex:<pattern>
220          * 4) u[.mech[/realm]]:<ID>
221          * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
222          * 6) <URL>
223          */
224
225         assert( in != NULL );
226         assert( !BER_BVISNULL( in ) );
227
228         Debug( LDAP_DEBUG_TRACE,
229                 "authzValidate: parsing %s\n", in->bv_val, 0, 0 );
230
231         /*
232          * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
233          * 3) dn.regex:<pattern>
234          *
235          * <DN> must pass DN normalization
236          */
237         if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
238                 bv.bv_val = in->bv_val + STRLENOF( "dn" );
239
240                 if ( bv.bv_val[ 0 ] == '.' ) {
241                         bv.bv_val++;
242
243                         if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
244                                 bv.bv_val += STRLENOF( "exact:" );
245                                 scope = LDAP_X_SCOPE_EXACT;
246
247                         } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
248                                 bv.bv_val += STRLENOF( "regex:" );
249                                 scope = LDAP_X_SCOPE_REGEX;
250
251                         } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
252                                 bv.bv_val += STRLENOF( "children:" );
253                                 scope = LDAP_X_SCOPE_CHILDREN;
254
255                         } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
256                                 bv.bv_val += STRLENOF( "subtree:" );
257                                 scope = LDAP_X_SCOPE_SUBTREE;
258
259                         } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
260                                 bv.bv_val += STRLENOF( "onelevel:" );
261                                 scope = LDAP_X_SCOPE_ONELEVEL;
262
263                         } else {
264                                 return LDAP_INVALID_SYNTAX;
265                         }
266
267                 } else {
268                         if ( bv.bv_val[ 0 ] != ':' ) {
269                                 return LDAP_INVALID_SYNTAX;
270                         }
271                         scope = LDAP_X_SCOPE_EXACT;
272                         bv.bv_val++;
273                 }
274
275                 bv.bv_val += strspn( bv.bv_val, " " );
276                 /* jump here in case no type specification was present
277                  * and uri was not an URI... HEADS-UP: assuming EXACT */
278 is_dn:          bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
279
280                 /* a single '*' means any DN without using regexes */
281                 if ( ber_bvccmp( &bv, '*' ) ) {
282                         /* LDAP_X_SCOPE_USERS */
283                         return LDAP_SUCCESS;
284                 }
285
286                 switch ( scope ) {
287                 case LDAP_X_SCOPE_EXACT:
288                 case LDAP_X_SCOPE_CHILDREN:
289                 case LDAP_X_SCOPE_SUBTREE:
290                 case LDAP_X_SCOPE_ONELEVEL:
291                         return dnValidate( NULL, &bv );
292
293                 case LDAP_X_SCOPE_REGEX:
294                         return LDAP_SUCCESS;
295                 }
296
297                 return rc;
298
299         /*
300          * 4) u[.mech[/realm]]:<ID>
301          */
302         } else if ( ( in->bv_val[ 0 ] == 'u' || in->bv_val[ 0 ] == 'U' )
303                         && ( in->bv_val[ 1 ] == ':' 
304                                 || in->bv_val[ 1 ] == '/' 
305                                 || in->bv_val[ 1 ] == '.' ) )
306         {
307                 char            buf[ SLAP_LDAPDN_MAXLEN ];
308                 struct berval   id,
309                                 user = BER_BVNULL,
310                                 realm = BER_BVNULL,
311                                 mech = BER_BVNULL;
312
313                 if ( sizeof( buf ) <= in->bv_len ) {
314                         return LDAP_INVALID_SYNTAX;
315                 }
316
317                 id.bv_len = in->bv_len;
318                 id.bv_val = buf;
319                 strncpy( buf, in->bv_val, sizeof( buf ) );
320
321                 rc = slap_parse_user( &id, &user, &realm, &mech );
322                 if ( rc != LDAP_SUCCESS ) {
323                         return LDAP_INVALID_SYNTAX;
324                 }
325
326                 return rc;
327
328         /*
329          * 5) group[/groupClass[/memberAttr]]:<DN>
330          *
331          * <groupClass> defaults to "groupOfNames"
332          * <memberAttr> defaults to "member"
333          * 
334          * <DN> must pass DN normalization
335          */
336         } else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
337         {
338                 struct berval   group_dn = BER_BVNULL,
339                                 group_oc = BER_BVNULL,
340                                 member_at = BER_BVNULL;
341
342                 bv.bv_val = in->bv_val + STRLENOF( "group" );
343                 bv.bv_len = in->bv_len - STRLENOF( "group" );
344                 group_dn.bv_val = ber_bvchr( &bv, ':' );
345                 if ( group_dn.bv_val == NULL ) {
346                         /* last chance: assume it's a(n exact) DN ... */
347                         bv.bv_val = in->bv_val;
348                         scope = LDAP_X_SCOPE_EXACT;
349                         goto is_dn;
350                 }
351                 
352                 /*
353                  * FIXME: we assume that "member" and "groupOfNames"
354                  * are present in schema...
355                  */
356                 if ( bv.bv_val[ 0 ] == '/' ) {
357                         group_oc.bv_val = &bv.bv_val[ 1 ];
358                         group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
359
360                         member_at.bv_val = ber_bvchr( &group_oc, '/' );
361                         if ( member_at.bv_val ) {
362                                 AttributeDescription    *ad = NULL;
363                                 const char              *text = NULL;
364
365                                 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
366                                 member_at.bv_val++;
367                                 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
368                                 rc = slap_bv2ad( &member_at, &ad, &text );
369                                 if ( rc != LDAP_SUCCESS ) {
370                                         return rc;
371                                 }
372                         }
373
374                         if ( oc_bvfind( &group_oc ) == NULL ) {
375                                 return LDAP_INVALID_SYNTAX;
376                         }
377                 }
378
379                 group_dn.bv_val++;
380                 group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
381
382                 rc = dnValidate( NULL, &group_dn );
383                 if ( rc != LDAP_SUCCESS ) {
384                         return rc;
385                 }
386
387                 return rc;
388         }
389
390         /*
391          * ldap:///<base>??<scope>?<filter>
392          * <scope> ::= {base|one|subtree}
393          *
394          * <scope> defaults to "base"
395          * <base> must pass DN normalization
396          * <filter> must pass str2filter()
397          */
398         rc = ldap_url_parse( in->bv_val, &ludp );
399         switch ( rc ) {
400         case LDAP_URL_SUCCESS:
401                 /* FIXME: the check is pedantic, but I think it's necessary,
402                  * because people tend to use things like ldaps:// which
403                  * gives the idea SSL is being used.  Maybe we could
404                  * accept ldapi:// as well, but the point is that we use
405                  * an URL as an easy means to define bits of a search with
406                  * little parsing.
407                  */
408                 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
409                         /*
410                          * must be ldap:///
411                          */
412                         rc = LDAP_INVALID_SYNTAX;
413                         goto done;
414                 }
415                 break;
416
417         case LDAP_URL_ERR_BADSCHEME:
418                 /*
419                  * last chance: assume it's a(n exact) DN ...
420                  *
421                  * NOTE: must pass DN normalization
422                  */
423                 ldap_free_urldesc( ludp );
424                 bv.bv_val = in->bv_val;
425                 scope = LDAP_X_SCOPE_EXACT;
426                 goto is_dn;
427
428         default:
429                 rc = LDAP_INVALID_SYNTAX;
430                 goto done;
431         }
432
433         if ( ( ludp->lud_host && *ludp->lud_host )
434                 || ludp->lud_attrs || ludp->lud_exts )
435         {
436                 /* host part must be empty */
437                 /* attrs and extensions parts must be empty */
438                 rc = LDAP_INVALID_SYNTAX;
439                 goto done;
440         }
441
442         /* Grab the filter */
443         if ( ludp->lud_filter ) {
444                 Filter  *f = str2filter( ludp->lud_filter );
445                 if ( f == NULL ) {
446                         rc = LDAP_INVALID_SYNTAX;
447                         goto done;
448                 }
449                 filter_free( f );
450         }
451
452         /* Grab the searchbase */
453         assert( ludp->lud_dn != NULL );
454         ber_str2bv( ludp->lud_dn, 0, 0, &bv );
455         rc = dnValidate( NULL, &bv );
456
457 done:
458         ldap_free_urldesc( ludp );
459         return( rc );
460 }
461
462 #if 0
463 int
464 authzMatch(
465         int             *matchp,
466         slap_mask_t     flags,
467         Syntax          *syntax,
468         MatchingRule    *mr,
469         struct berval   *value,
470         void            *assertedValue )
471 {
472         return octetStringMatch( matchp, flags, syntax, mr, value, assertedValue );
473 }
474 #endif
475
476 static int
477 authzPrettyNormal(
478         struct berval   *val,
479         struct berval   *normalized,
480         void            *ctx,
481         int             normalize )
482 {
483         struct berval   bv;
484         int             rc = LDAP_INVALID_SYNTAX;
485         LDAPURLDesc     *ludp = NULL;
486         char            *lud_dn = NULL,
487                         *lud_filter = NULL;
488         int             scope = -1;
489
490         /*
491          * 1) <DN>
492          * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
493          * 3) dn.regex:<pattern>
494          * 4) u[.mech[/realm]]:<ID>
495          * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
496          * 6) <URL>
497          */
498
499         assert( val != NULL );
500         assert( !BER_BVISNULL( val ) );
501
502         /*
503          * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
504          * 3) dn.regex:<pattern>
505          *
506          * <DN> must pass DN normalization
507          */
508         if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
509                 struct berval   out = BER_BVNULL,
510                                 prefix = BER_BVNULL;
511                 char            *ptr;
512
513                 bv.bv_val = val->bv_val + STRLENOF( "dn" );
514
515                 if ( bv.bv_val[ 0 ] == '.' ) {
516                         bv.bv_val++;
517
518                         if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
519                                 bv.bv_val += STRLENOF( "exact:" );
520                                 scope = LDAP_X_SCOPE_EXACT;
521
522                         } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
523                                 bv.bv_val += STRLENOF( "regex:" );
524                                 scope = LDAP_X_SCOPE_REGEX;
525
526                         } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
527                                 bv.bv_val += STRLENOF( "children:" );
528                                 scope = LDAP_X_SCOPE_CHILDREN;
529
530                         } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
531                                 bv.bv_val += STRLENOF( "subtree:" );
532                                 scope = LDAP_X_SCOPE_SUBTREE;
533
534                         } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
535                                 bv.bv_val += STRLENOF( "onelevel:" );
536                                 scope = LDAP_X_SCOPE_ONELEVEL;
537
538                         } else {
539                                 return LDAP_INVALID_SYNTAX;
540                         }
541
542                 } else {
543                         if ( bv.bv_val[ 0 ] != ':' ) {
544                                 return LDAP_INVALID_SYNTAX;
545                         }
546                         scope = LDAP_X_SCOPE_EXACT;
547                         bv.bv_val++;
548                 }
549
550                 bv.bv_val += strspn( bv.bv_val, " " );
551                 /* jump here in case no type specification was present
552                  * and uri was not an URI... HEADS-UP: assuming EXACT */
553 is_dn:          bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
554
555                 /* a single '*' means any DN without using regexes */
556                 if ( ber_bvccmp( &bv, '*' ) ) {
557                         ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
558                         return LDAP_SUCCESS;
559                 }
560
561                 switch ( scope ) {
562                 case LDAP_X_SCOPE_EXACT:
563                 case LDAP_X_SCOPE_CHILDREN:
564                 case LDAP_X_SCOPE_SUBTREE:
565                 case LDAP_X_SCOPE_ONELEVEL:
566                         if ( normalize ) {
567                                 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
568                         } else {
569                                 rc = dnPretty( NULL, &bv, &out, ctx );
570                         }
571                         if( rc != LDAP_SUCCESS ) {
572                                 return LDAP_INVALID_SYNTAX;
573                         }
574                         break;
575
576                 case LDAP_X_SCOPE_REGEX:
577                         normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
578                         normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
579                         ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
580                         ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
581                         ptr[ 0 ] = '\0';
582                         return LDAP_SUCCESS;
583
584                 default:
585                         return LDAP_INVALID_SYNTAX;
586                 }
587
588                 /* prepare prefix */
589                 switch ( scope ) {
590                 case LDAP_X_SCOPE_EXACT:
591                         BER_BVSTR( &prefix, "dn:" );
592                         break;
593
594                 case LDAP_X_SCOPE_CHILDREN:
595                         BER_BVSTR( &prefix, "dn.children:" );
596                         break;
597
598                 case LDAP_X_SCOPE_SUBTREE:
599                         BER_BVSTR( &prefix, "dn.subtree:" );
600                         break;
601
602                 case LDAP_X_SCOPE_ONELEVEL:
603                         BER_BVSTR( &prefix, "dn.onelevel:" );
604                         break;
605
606                 default:
607                         assert( 0 );
608                         break;
609                 }
610
611                 normalized->bv_len = prefix.bv_len + out.bv_len;
612                 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
613                 
614                 ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
615                 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
616                 ptr[ 0 ] = '\0';
617                 ber_memfree_x( out.bv_val, ctx );
618
619                 return LDAP_SUCCESS;
620
621         /*
622          * 4) u[.mech[/realm]]:<ID>
623          */
624         } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
625                         && ( val->bv_val[ 1 ] == ':' 
626                                 || val->bv_val[ 1 ] == '/' 
627                                 || val->bv_val[ 1 ] == '.' ) )
628         {
629                 char            buf[ SLAP_LDAPDN_MAXLEN ];
630                 struct berval   id,
631                                 user = BER_BVNULL,
632                                 realm = BER_BVNULL,
633                                 mech = BER_BVNULL;
634
635                 if ( sizeof( buf ) <= val->bv_len ) {
636                         return LDAP_INVALID_SYNTAX;
637                 }
638
639                 id.bv_len = val->bv_len;
640                 id.bv_val = buf;
641                 strncpy( buf, val->bv_val, sizeof( buf ) );
642
643                 rc = slap_parse_user( &id, &user, &realm, &mech );
644                 if ( rc != LDAP_SUCCESS ) {
645                         return LDAP_INVALID_SYNTAX;
646                 }
647
648                 ber_dupbv_x( normalized, val, ctx );
649
650                 return rc;
651
652         /*
653          * 5) group[/groupClass[/memberAttr]]:<DN>
654          *
655          * <groupClass> defaults to "groupOfNames"
656          * <memberAttr> defaults to "member"
657          * 
658          * <DN> must pass DN normalization
659          */
660         } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
661         {
662                 struct berval   group_dn = BER_BVNULL,
663                                 group_oc = BER_BVNULL,
664                                 member_at = BER_BVNULL,
665                                 out = BER_BVNULL;
666                 char            *ptr;
667
668                 bv.bv_val = val->bv_val + STRLENOF( "group" );
669                 bv.bv_len = val->bv_len - STRLENOF( "group" );
670                 group_dn.bv_val = ber_bvchr( &bv, ':' );
671                 if ( group_dn.bv_val == NULL ) {
672                         /* last chance: assume it's a(n exact) DN ... */
673                         bv.bv_val = val->bv_val;
674                         scope = LDAP_X_SCOPE_EXACT;
675                         goto is_dn;
676                 }
677
678                 /*
679                  * FIXME: we assume that "member" and "groupOfNames"
680                  * are present in schema...
681                  */
682                 if ( bv.bv_val[ 0 ] == '/' ) {
683                         ObjectClass             *oc = NULL;
684
685                         group_oc.bv_val = &bv.bv_val[ 1 ];
686                         group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
687
688                         member_at.bv_val = ber_bvchr( &group_oc, '/' );
689                         if ( member_at.bv_val ) {
690                                 AttributeDescription    *ad = NULL;
691                                 const char              *text = NULL;
692
693                                 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
694                                 member_at.bv_val++;
695                                 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
696                                 rc = slap_bv2ad( &member_at, &ad, &text );
697                                 if ( rc != LDAP_SUCCESS ) {
698                                         return rc;
699                                 }
700
701                                 member_at = ad->ad_cname;
702
703                         }
704
705                         oc = oc_bvfind( &group_oc );
706                         if ( oc == NULL ) {
707                                 return LDAP_INVALID_SYNTAX;
708                         }
709
710                         group_oc = oc->soc_cname;
711                 }
712
713                 group_dn.bv_val++;
714                 group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
715
716                 if ( normalize ) {
717                         rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
718                 } else {
719                         rc = dnPretty( NULL, &group_dn, &out, ctx );
720                 }
721                 if ( rc != LDAP_SUCCESS ) {
722                         return rc;
723                 }
724
725                 normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
726                 if ( !BER_BVISNULL( &group_oc ) ) {
727                         normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
728                         if ( !BER_BVISNULL( &member_at ) ) {
729                                 normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
730                         }
731                 }
732
733                 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
734                 ptr = lutil_strcopy( normalized->bv_val, "group" );
735                 if ( !BER_BVISNULL( &group_oc ) ) {
736                         ptr[ 0 ] = '/';
737                         ptr++;
738                         ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
739                         if ( !BER_BVISNULL( &member_at ) ) {
740                                 ptr[ 0 ] = '/';
741                                 ptr++;
742                                 ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
743                         }
744                 }
745                 ptr[ 0 ] = ':';
746                 ptr++;
747                 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
748                 ptr[ 0 ] = '\0';
749                 ber_memfree_x( out.bv_val, ctx );
750
751                 return rc;
752         }
753
754         /*
755          * ldap:///<base>??<scope>?<filter>
756          * <scope> ::= {base|one|subtree}
757          *
758          * <scope> defaults to "base"
759          * <base> must pass DN normalization
760          * <filter> must pass str2filter()
761          */
762         rc = ldap_url_parse( val->bv_val, &ludp );
763         switch ( rc ) {
764         case LDAP_URL_SUCCESS:
765                 /* FIXME: the check is pedantic, but I think it's necessary,
766                  * because people tend to use things like ldaps:// which
767                  * gives the idea SSL is being used.  Maybe we could
768                  * accept ldapi:// as well, but the point is that we use
769                  * an URL as an easy means to define bits of a search with
770                  * little parsing.
771                  */
772                 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
773                         /*
774                          * must be ldap:///
775                          */
776                         rc = LDAP_INVALID_SYNTAX;
777                         goto done;
778                 }
779
780                 AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
781                 break;
782
783         case LDAP_URL_ERR_BADSCHEME:
784                 /*
785                  * last chance: assume it's a(n exact) DN ...
786                  *
787                  * NOTE: must pass DN normalization
788                  */
789                 ldap_free_urldesc( ludp );
790                 bv.bv_val = val->bv_val;
791                 scope = LDAP_X_SCOPE_EXACT;
792                 goto is_dn;
793
794         default:
795                 rc = LDAP_INVALID_SYNTAX;
796                 goto done;
797         }
798
799         if ( ( ludp->lud_host && *ludp->lud_host )
800                 || ludp->lud_attrs || ludp->lud_exts )
801         {
802                 /* host part must be empty */
803                 /* attrs and extensions parts must be empty */
804                 rc = LDAP_INVALID_SYNTAX;
805                 goto done;
806         }
807
808         /* Grab the filter */
809         if ( ludp->lud_filter ) {
810                 struct berval   filterstr;
811                 Filter          *f;
812
813                 lud_filter = ludp->lud_filter;
814
815                 f = str2filter( lud_filter );
816                 if ( f == NULL ) {
817                         rc = LDAP_INVALID_SYNTAX;
818                         goto done;
819                 }
820                 filter2bv( f, &filterstr );
821                 filter_free( f );
822                 if ( BER_BVISNULL( &filterstr ) ) {
823                         rc = LDAP_INVALID_SYNTAX;
824                         goto done;
825                 }
826
827                 ludp->lud_filter = filterstr.bv_val;
828         }
829
830         /* Grab the searchbase */
831         assert( ludp->lud_dn != NULL );
832         if ( ludp->lud_dn ) {
833                 struct berval   out = BER_BVNULL;
834
835                 lud_dn = ludp->lud_dn;
836
837                 ber_str2bv( lud_dn, 0, 0, &bv );
838                 if ( normalize ) {
839                         rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
840                 } else {
841                         rc = dnPretty( NULL, &bv, &out, ctx );
842                 }
843
844                 if ( rc != LDAP_SUCCESS ) {
845                         goto done;
846                 }
847
848                 ludp->lud_dn = out.bv_val;
849         }
850
851         ludp->lud_port = 0;
852         normalized->bv_val = ldap_url_desc2str( ludp );
853         if ( normalized->bv_val ) {
854                 normalized->bv_len = strlen( normalized->bv_val );
855
856         } else {
857                 rc = LDAP_INVALID_SYNTAX;
858         }
859
860 done:
861         if ( lud_filter ) {
862                 if ( ludp->lud_filter != lud_filter ) {
863                         ber_memfree( ludp->lud_filter );
864                 }
865                 ludp->lud_filter = lud_filter;
866         }
867
868         if ( lud_dn ) {
869                 if ( ludp->lud_dn != lud_dn ) {
870                         ber_memfree( ludp->lud_dn );
871                 }
872                 ludp->lud_dn = lud_dn;
873         }
874
875         ldap_free_urldesc( ludp );
876
877         return( rc );
878 }
879
880 int
881 authzNormalize(
882         slap_mask_t     usage,
883         Syntax          *syntax,
884         MatchingRule    *mr,
885         struct berval   *val,
886         struct berval   *normalized,
887         void            *ctx )
888 {
889         int             rc;
890
891         Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
892                 val->bv_val, 0, 0 );
893
894         rc = authzPrettyNormal( val, normalized, ctx, 1 );
895
896         Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
897                 normalized->bv_val, rc, 0 );
898
899         return rc;
900 }
901
902 int
903 authzPretty(
904         Syntax *syntax,
905         struct berval *val,
906         struct berval *out,
907         void *ctx)
908 {
909         int             rc;
910
911         Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
912                 val->bv_val, 0, 0 );
913
914         rc = authzPrettyNormal( val, out, ctx, 0 );
915
916         Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
917                 out->bv_val, rc, 0 );
918
919         return rc;
920 }
921
922 #endif /* SLAP_AUTHZ_SYNTAX */
923
924 static int
925 slap_parseURI(
926         Operation       *op,
927         struct berval   *uri,
928         struct berval   *base,
929         struct berval   *nbase,
930         int             *scope,
931         Filter          **filter,
932         struct berval   *fstr,
933         int             normalize )
934 {
935         struct berval   bv;
936         int             rc;
937         LDAPURLDesc     *ludp;
938
939 #ifdef SLAP_ORDERED_PRETTYNORM
940         struct berval   idx;
941 #endif /* SLAP_ORDERED_PRETTYNORM */
942
943         assert( uri != NULL && !BER_BVISNULL( uri ) );
944         BER_BVZERO( base );
945         BER_BVZERO( nbase );
946         BER_BVZERO( fstr );
947         *scope = -1;
948         *filter = NULL;
949
950         Debug( LDAP_DEBUG_TRACE,
951                 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
952
953         rc = LDAP_PROTOCOL_ERROR;
954
955 #ifdef SLAP_ORDERED_PRETTYNORM
956         idx = *uri;
957         if ( idx.bv_val[ 0 ] == '{' ) {
958                 char    *ptr;
959
960                 ptr = ber_bvchr( &idx, '}' ) + 1;
961
962                 assert( ptr != (void *)1 );
963
964                 idx.bv_len -= ptr - idx.bv_val;
965                 idx.bv_val = ptr;
966                 uri = &idx;
967         }
968 #endif /* SLAP_ORDERED_PRETTYNORM */
969
970         /*
971          * dn[.<dnstyle>]:<dnpattern>
972          * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
973          *
974          * <dnstyle> defaults to "exact"
975          * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
976          */
977         if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
978                 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
979
980                 if ( bv.bv_val[ 0 ] == '.' ) {
981                         bv.bv_val++;
982
983                         if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
984                                 bv.bv_val += STRLENOF( "exact:" );
985                                 *scope = LDAP_X_SCOPE_EXACT;
986
987                         } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
988                                 bv.bv_val += STRLENOF( "regex:" );
989                                 *scope = LDAP_X_SCOPE_REGEX;
990
991                         } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
992                                 bv.bv_val += STRLENOF( "children:" );
993                                 *scope = LDAP_X_SCOPE_CHILDREN;
994
995                         } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
996                                 bv.bv_val += STRLENOF( "subtree:" );
997                                 *scope = LDAP_X_SCOPE_SUBTREE;
998
999                         } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
1000                                 bv.bv_val += STRLENOF( "onelevel:" );
1001                                 *scope = LDAP_X_SCOPE_ONELEVEL;
1002
1003                         } else {
1004                                 return LDAP_PROTOCOL_ERROR;
1005                         }
1006
1007                 } else {
1008                         if ( bv.bv_val[ 0 ] != ':' ) {
1009                                 return LDAP_PROTOCOL_ERROR;
1010                         }
1011                         *scope = LDAP_X_SCOPE_EXACT;
1012                         bv.bv_val++;
1013                 }
1014
1015                 bv.bv_val += strspn( bv.bv_val, " " );
1016                 /* jump here in case no type specification was present
1017                  * and uri was not an URI... HEADS-UP: assuming EXACT */
1018 is_dn:          bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
1019
1020                 /* a single '*' means any DN without using regexes */
1021                 if ( ber_bvccmp( &bv, '*' ) ) {
1022                         *scope = LDAP_X_SCOPE_USERS;
1023                 }
1024
1025                 switch ( *scope ) {
1026                 case LDAP_X_SCOPE_EXACT:
1027                 case LDAP_X_SCOPE_CHILDREN:
1028                 case LDAP_X_SCOPE_SUBTREE:
1029                 case LDAP_X_SCOPE_ONELEVEL:
1030                         if ( normalize ) {
1031                                 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
1032                                 if( rc != LDAP_SUCCESS ) {
1033                                         *scope = -1;
1034                                 }
1035                         } else {
1036                                 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1037                                 rc = LDAP_SUCCESS;
1038                         }
1039                         break;
1040
1041                 case LDAP_X_SCOPE_REGEX:
1042                         ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1043
1044                 case LDAP_X_SCOPE_USERS:
1045                         rc = LDAP_SUCCESS;
1046                         break;
1047
1048                 default:
1049                         *scope = -1;
1050                         break;
1051                 }
1052
1053                 return rc;
1054
1055         /*
1056          * u:<uid>
1057          */
1058         } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
1059                         && ( uri->bv_val[ 1 ] == ':' 
1060                                 || uri->bv_val[ 1 ] == '/' 
1061                                 || uri->bv_val[ 1 ] == '.' ) )
1062         {
1063                 Connection      c = *op->o_conn;
1064                 char            buf[ SLAP_LDAPDN_MAXLEN ];
1065                 struct berval   id,
1066                                 user = BER_BVNULL,
1067                                 realm = BER_BVNULL,
1068                                 mech = BER_BVNULL;
1069
1070                 if ( sizeof( buf ) <= uri->bv_len ) {
1071                         return LDAP_INVALID_SYNTAX;
1072                 }
1073
1074                 id.bv_len = uri->bv_len;
1075                 id.bv_val = buf;
1076                 strncpy( buf, uri->bv_val, sizeof( buf ) );
1077
1078                 rc = slap_parse_user( &id, &user, &realm, &mech );
1079                 if ( rc != LDAP_SUCCESS ) {
1080                         return rc;
1081                 }
1082
1083                 if ( !BER_BVISNULL( &mech ) ) {
1084                         c.c_sasl_bind_mech = mech;
1085                 } else {
1086                         BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1087                 }
1088                 
1089                 rc = slap_sasl_getdn( &c, op, &user,
1090                                 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1091
1092                 if ( rc == LDAP_SUCCESS ) {
1093                         *scope = LDAP_X_SCOPE_EXACT;
1094                 }
1095
1096                 return rc;
1097
1098         /*
1099          * group[/<groupoc>[/<groupat>]]:<groupdn>
1100          *
1101          * groupoc defaults to "groupOfNames"
1102          * groupat defaults to "member"
1103          * 
1104          * <groupdn> must pass DN normalization
1105          */
1106         } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1107         {
1108                 struct berval   group_dn = BER_BVNULL,
1109                                 group_oc = BER_BVNULL,
1110                                 member_at = BER_BVNULL;
1111                 char            *tmp;
1112
1113                 bv.bv_val = uri->bv_val + STRLENOF( "group" );
1114                 bv.bv_len = uri->bv_len - STRLENOF( "group" );
1115                 group_dn.bv_val = ber_bvchr( &bv, ':' );
1116                 if ( group_dn.bv_val == NULL ) {
1117                         /* last chance: assume it's a(n exact) DN ... */
1118                         bv.bv_val = uri->bv_val;
1119                         *scope = LDAP_X_SCOPE_EXACT;
1120                         goto is_dn;
1121                 }
1122                 
1123                 if ( bv.bv_val[ 0 ] == '/' ) {
1124                         group_oc.bv_val = &bv.bv_val[ 1 ];
1125                         group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
1126
1127                         member_at.bv_val = ber_bvchr( &group_oc, '/' );
1128                         if ( member_at.bv_val ) {
1129                                 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
1130                                 member_at.bv_val++;
1131                                 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
1132
1133                         } else {
1134                                 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1135                         }
1136
1137                 } else {
1138                         BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
1139                         BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1140                 }
1141                 group_dn.bv_val++;
1142                 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1143
1144                 if ( normalize ) {
1145                         rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1146                         if ( rc != LDAP_SUCCESS ) {
1147                                 *scope = -1;
1148                                 return rc;
1149                         }
1150                 } else {
1151                         ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1152                         rc = LDAP_SUCCESS;
1153                 }
1154                 *scope = LDAP_X_SCOPE_GROUP;
1155
1156                 /* FIXME: caller needs to add value of member attribute
1157                  * and close brackets twice */
1158                 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
1159                         + group_oc.bv_len + member_at.bv_len;
1160                 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
1161
1162                 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
1163                                 STRLENOF( "(&(objectClass=" /* )) */ ) );
1164                 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
1165                 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
1166                                 STRLENOF( /* ( */ ")(" /* ) */ ) );
1167                 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
1168                 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
1169
1170                 return rc;
1171         }
1172
1173         /*
1174          * ldap:///<base>??<scope>?<filter>
1175          * <scope> ::= {base|one|subtree}
1176          *
1177          * <scope> defaults to "base"
1178          * <base> must pass DN normalization
1179          * <filter> must pass str2filter()
1180          */
1181         rc = ldap_url_parse( uri->bv_val, &ludp );
1182         switch ( rc ) {
1183         case LDAP_URL_SUCCESS:
1184                 /* FIXME: the check is pedantic, but I think it's necessary,
1185                  * because people tend to use things like ldaps:// which
1186                  * gives the idea SSL is being used.  Maybe we could
1187                  * accept ldapi:// as well, but the point is that we use
1188                  * an URL as an easy means to define bits of a search with
1189                  * little parsing.
1190                  */
1191                 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1192                         /*
1193                          * must be ldap:///
1194                          */
1195                         rc = LDAP_PROTOCOL_ERROR;
1196                         goto done;
1197                 }
1198                 break;
1199
1200         case LDAP_URL_ERR_BADSCHEME:
1201                 /*
1202                  * last chance: assume it's a(n exact) DN ...
1203                  *
1204                  * NOTE: must pass DN normalization
1205                  */
1206                 ldap_free_urldesc( ludp );
1207                 bv.bv_val = uri->bv_val;
1208                 *scope = LDAP_X_SCOPE_EXACT;
1209                 goto is_dn;
1210
1211         default:
1212                 rc = LDAP_PROTOCOL_ERROR;
1213                 goto done;
1214         }
1215
1216         if ( ( ludp->lud_host && *ludp->lud_host )
1217                 || ludp->lud_attrs || ludp->lud_exts )
1218         {
1219                 /* host part must be empty */
1220                 /* attrs and extensions parts must be empty */
1221                 rc = LDAP_PROTOCOL_ERROR;
1222                 goto done;
1223         }
1224
1225         /* Grab the scope */
1226         *scope = ludp->lud_scope;
1227
1228         /* Grab the filter */
1229         if ( ludp->lud_filter ) {
1230                 *filter = str2filter_x( op, ludp->lud_filter );
1231                 if ( *filter == NULL ) {
1232                         rc = LDAP_PROTOCOL_ERROR;
1233                         goto done;
1234                 }
1235                 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1236         }
1237
1238         /* Grab the searchbase */
1239         ber_str2bv( ludp->lud_dn, 0, 0, base );
1240         if ( normalize ) {
1241                 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1242         } else {
1243                 ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1244                 rc = LDAP_SUCCESS;
1245         }
1246
1247 done:
1248         if( rc != LDAP_SUCCESS ) {
1249                 if( *filter ) filter_free_x( op, *filter );
1250                 BER_BVZERO( base );
1251                 BER_BVZERO( fstr );
1252         } else {
1253                 /* Don't free these, return them to caller */
1254                 ludp->lud_filter = NULL;
1255                 ludp->lud_dn = NULL;
1256         }
1257
1258         ldap_free_urldesc( ludp );
1259         return( rc );
1260 }
1261
1262 #ifndef SLAP_AUTH_REWRITE
1263 static int slap_sasl_rx_off(char *rep, int *off)
1264 {
1265         const char *c;
1266         int n;
1267
1268         /* Precompile replace pattern. Find the $<n> placeholders */
1269         off[0] = -2;
1270         n = 1;
1271         for ( c = rep;   *c;  c++ ) {
1272                 if ( *c == '\\' && c[1] ) {
1273                         c++;
1274                         continue;
1275                 }
1276                 if ( *c == '$' ) {
1277                         if ( n == SASLREGEX_REPLACE ) {
1278                                 Debug( LDAP_DEBUG_ANY,
1279                                         "SASL replace pattern %s has too many $n "
1280                                                 "placeholders (max %d)\n",
1281                                         rep, SASLREGEX_REPLACE, 0 );
1282
1283                                 return( LDAP_OTHER );
1284                         }
1285                         off[n] = c - rep;
1286                         n++;
1287                 }
1288         }
1289
1290         /* Final placeholder, after the last $n */
1291         off[n] = c - rep;
1292         n++;
1293         off[n] = -1;
1294         return( LDAP_SUCCESS );
1295 }
1296 #endif /* ! SLAP_AUTH_REWRITE */
1297
1298 #ifdef SLAP_AUTH_REWRITE
1299 int slap_sasl_rewrite_config( 
1300                 const char      *fname,
1301                 int             lineno,
1302                 int             argc,
1303                 char            **argv
1304 )
1305 {
1306         int     rc;
1307         char    *savearg0;
1308
1309         /* init at first call */
1310         if ( sasl_rwinfo == NULL ) {
1311                 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1312         }
1313
1314         /* strip "authid-" prefix for parsing */
1315         savearg0 = argv[0];
1316         argv[0] += STRLENOF( "authid-" );
1317         rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1318         argv[0] = savearg0;
1319
1320         return rc;
1321 }
1322
1323 int slap_sasl_rewrite_destroy( void )
1324 {
1325         if ( sasl_rwinfo ) {
1326                 rewrite_info_delete( &sasl_rwinfo );
1327                 sasl_rwinfo = NULL;
1328         }
1329
1330         return 0;
1331 }
1332
1333 int slap_sasl_regexp_rewrite_config(
1334                 const char      *fname,
1335                 int             lineno,
1336                 const char      *match,
1337                 const char      *replace,
1338                 const char      *context )
1339 {
1340         int     rc;
1341         char    *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1342
1343         /* init at first call */
1344         if ( sasl_rwinfo == NULL ) {
1345                 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1346                 char *argvContext[] = { "rewriteContext", NULL, NULL };
1347
1348                 /* initialize rewrite engine */
1349                 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1350
1351                 /* switch on rewrite engine */
1352                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1353                 if (rc != LDAP_SUCCESS) {
1354                         return rc;
1355                 }
1356
1357                 /* create generic authid context */
1358                 argvContext[1] = AUTHID_CONTEXT;
1359                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1360                 if (rc != LDAP_SUCCESS) {
1361                         return rc;
1362                 }
1363         }
1364
1365         argvRule[1] = (char *)match;
1366         argvRule[2] = (char *)replace;
1367         rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1368
1369         return rc;
1370 }
1371 #endif /* SLAP_AUTH_REWRITE */
1372
1373 int slap_sasl_regexp_config( const char *match, const char *replace )
1374 {
1375         int rc;
1376         SaslRegexp_t *reg;
1377
1378         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1379           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1380
1381         reg = &SaslRegexp[nSaslRegexp];
1382
1383         reg->sr_match = ch_strdup( match );
1384         reg->sr_replace = ch_strdup( replace );
1385
1386 #ifdef SLAP_AUTH_REWRITE
1387         rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1388                         match, replace, AUTHID_CONTEXT );
1389         if ( rc == LDAP_SUCCESS ) nSaslRegexp++;
1390         return rc;
1391 #else /* ! SLAP_AUTH_REWRITE */
1392
1393         /* Precompile matching pattern */
1394         rc = regcomp( &reg->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
1395         if ( rc ) {
1396                 Debug( LDAP_DEBUG_ANY,
1397                 "SASL match pattern %s could not be compiled by regexp engine\n",
1398                 reg->sr_match, 0, 0 );
1399
1400 #ifdef ENABLE_REWRITE
1401         /* Dummy block to force symbol references in librewrite */
1402         if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1403                 rewrite_info_init( 0 );
1404         }
1405 #endif
1406                 return( LDAP_OTHER );
1407         }
1408
1409         rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
1410         if ( rc != LDAP_SUCCESS ) return rc;
1411
1412         nSaslRegexp++;
1413         return( LDAP_SUCCESS );
1414 #endif /* ! SLAP_AUTH_REWRITE */
1415 }
1416
1417 void slap_sasl_regexp_unparse( BerVarray *out )
1418 {
1419         int i;
1420         BerVarray bva = NULL;
1421         char ibuf[32], *ptr;
1422         struct berval idx;
1423
1424         if ( !nSaslRegexp ) return;
1425
1426         idx.bv_val = ibuf;
1427         bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1428         BER_BVZERO(bva+nSaslRegexp);
1429         for ( i=0; i<nSaslRegexp; i++ ) {
1430                 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1431                 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1432                         strlen( SaslRegexp[i].sr_replace ) + 5;
1433                 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1434                 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1435                 *ptr++ = '"';
1436                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1437                 ptr = lutil_strcopy( ptr, "\" \"" );
1438                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1439                 *ptr++ = '"';
1440                 *ptr = '\0';
1441         }
1442         *out = bva;
1443 }
1444
1445 #ifndef SLAP_AUTH_REWRITE
1446 /* Perform replacement on regexp matches */
1447 static void slap_sasl_rx_exp(
1448         const char *rep,
1449         const int *off,
1450         regmatch_t *str,
1451         const char *saslname,
1452         struct berval *out,
1453         void *ctx )
1454 {
1455         int i, n, len, insert;
1456
1457         /* Get the total length of the final URI */
1458
1459         n=1;
1460         len = 0;
1461         while( off[n] >= 0 ) {
1462                 /* Len of next section from replacement string (x,y,z above) */
1463                 len += off[n] - off[n-1] - 2;
1464                 if( off[n+1] < 0)
1465                         break;
1466
1467                 /* Len of string from saslname that matched next $i  (b,d above) */
1468                 i = rep[ off[n] + 1 ]   - '0';
1469                 len += str[i].rm_eo - str[i].rm_so;
1470                 n++;
1471         }
1472         out->bv_val = slap_sl_malloc( len + 1, ctx );
1473         out->bv_len = len;
1474
1475         /* Fill in URI with replace string, replacing $i as we go */
1476         n=1;
1477         insert = 0;
1478         while( off[n] >= 0) {
1479                 /* Paste in next section from replacement string (x,y,z above) */
1480                 len = off[n] - off[n-1] - 2;
1481                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1482                 insert += len;
1483                 if( off[n+1] < 0)
1484                         break;
1485
1486                 /* Paste in string from saslname that matched next $i  (b,d above) */
1487                 i = rep[ off[n] + 1 ]   - '0';
1488                 len = str[i].rm_eo - str[i].rm_so;
1489                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1490                 insert += len;
1491
1492                 n++;
1493         }
1494
1495         out->bv_val[insert] = '\0';
1496 }
1497 #endif /* ! SLAP_AUTH_REWRITE */
1498
1499 /* Take the passed in SASL name and attempt to convert it into an
1500    LDAP URI to find the matching LDAP entry, using the pattern matching
1501    strings given in the saslregexp config file directive(s) */
1502
1503 static int slap_authz_regexp( struct berval *in, struct berval *out,
1504                 int flags, void *ctx )
1505 {
1506 #ifdef SLAP_AUTH_REWRITE
1507         const char      *context = AUTHID_CONTEXT;
1508
1509         if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1510                 return 0;
1511         }
1512
1513         /* FIXME: if aware of authc/authz mapping, 
1514          * we could use different contexts ... */
1515         switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 
1516                                 &out->bv_val ) )
1517         {
1518         case REWRITE_REGEXEC_OK:
1519                 if ( !BER_BVISNULL( out ) ) {
1520                         char *val = out->bv_val;
1521                         ber_str2bv_x( val, 0, 1, out, ctx );
1522                         if ( val != in->bv_val ) {
1523                                 free( val );
1524                         }
1525                 } else {
1526                         ber_dupbv_x( out, in, ctx );
1527                 }
1528                 Debug( LDAP_DEBUG_ARGS,
1529                         "[rw] %s: \"%s\" -> \"%s\"\n",
1530                         context, in->bv_val, out->bv_val );             
1531                 return 1;
1532                 
1533         case REWRITE_REGEXEC_UNWILLING:
1534         case REWRITE_REGEXEC_ERR:
1535         default:
1536                 return 0;
1537         }
1538
1539 #else /* ! SLAP_AUTH_REWRITE */
1540         char *saslname = in->bv_val;
1541         SaslRegexp_t *reg;
1542         regmatch_t sr_strings[SASLREGEX_REPLACE];       /* strings matching $1,$2 ... */
1543         int i;
1544
1545         memset( out, 0, sizeof( *out ) );
1546
1547         Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1548            saslname, 0, 0 );
1549
1550         if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1551                 return( 0 );
1552         }
1553
1554         /* Match the normalized SASL name to the saslregexp patterns */
1555         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
1556                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
1557                   sr_strings, 0)  == 0 )
1558                         break;
1559         }
1560
1561         if( i >= nSaslRegexp ) return( 0 );
1562
1563         /*
1564          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1565          * replace pattern of the form "x$1y$2z". The returned string needs
1566          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1567          */
1568         slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1569                 sr_strings, saslname, out, ctx );
1570
1571         Debug( LDAP_DEBUG_TRACE,
1572                 "slap_authz_regexp: converted SASL name to %s\n",
1573                 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1574
1575         return( 1 );
1576 #endif /* ! SLAP_AUTH_REWRITE */
1577 }
1578
1579 /* This callback actually does some work...*/
1580 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1581 {
1582         struct berval *ndn = op->o_callback->sc_private;
1583
1584         if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1585
1586         /* We only want to be called once */
1587         if ( !BER_BVISNULL( ndn ) ) {
1588                 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1589                 BER_BVZERO( ndn );
1590
1591                 Debug( LDAP_DEBUG_TRACE,
1592                         "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1593                         op->o_log_prefix, 0, 0 );
1594                 return LDAP_UNAVAILABLE; /* short-circuit the search */
1595         }
1596
1597         ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1598         return LDAP_SUCCESS;
1599 }
1600
1601
1602 typedef struct smatch_info {
1603         struct berval *dn;
1604         int match;
1605 } smatch_info;
1606
1607 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1608 {
1609         smatch_info *sm = o->o_callback->sc_private;
1610
1611         if (rs->sr_type != REP_SEARCH) return 0;
1612
1613         if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1614                 sm->match = 1;
1615                 return LDAP_UNAVAILABLE;        /* short-circuit the search */
1616         }
1617
1618         return 0;
1619 }
1620
1621 int
1622 slap_sasl_matches( Operation *op, BerVarray rules,
1623                 struct berval *assertDN, struct berval *authc )
1624 {
1625         int     rc = LDAP_INAPPROPRIATE_AUTH;
1626
1627         if ( rules != NULL ) {
1628                 int     i;
1629
1630                 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1631                         rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1632                         if ( rc == LDAP_SUCCESS ) break;
1633                 }
1634         }
1635         
1636         return rc;
1637 }
1638
1639 /*
1640  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1641  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1642  * the rule must be used as an internal search for entries. If that search
1643  * returns the *assertDN entry, the match is successful.
1644  *
1645  * The assertDN should not have the dn: prefix
1646  */
1647
1648 static int
1649 slap_sasl_match( Operation *opx, struct berval *rule,
1650         struct berval *assertDN, struct berval *authc )
1651 {
1652         int rc; 
1653         regex_t reg;
1654         smatch_info sm;
1655         slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1656         Operation op = {0};
1657         SlapReply rs = {REP_RESULT};
1658         struct berval base = BER_BVNULL;
1659
1660         sm.dn = assertDN;
1661         sm.match = 0;
1662         cb.sc_private = &sm;
1663
1664         Debug( LDAP_DEBUG_TRACE,
1665            "===>slap_sasl_match: comparing DN %s to rule %s\n",
1666                 assertDN->bv_val, rule->bv_val, 0 );
1667
1668         /* NOTE: don't normalize rule if authz syntax is enabled */
1669         rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1670                 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 
1671 #ifdef SLAP_AUTHZ_SYNTAX
1672                 0
1673 #else /* ! SLAP_AUTHZ_SYNTAX */
1674                 1
1675 #endif /* ! SLAP_AUTHZ_SYNTAX */
1676                 );
1677
1678         if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1679
1680         switch ( op.ors_scope ) {
1681         case LDAP_X_SCOPE_EXACT:
1682 exact_match:
1683                 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1684                         rc = LDAP_SUCCESS;
1685                 } else {
1686                         rc = LDAP_INAPPROPRIATE_AUTH;
1687                 }
1688                 goto CONCLUDED;
1689
1690         case LDAP_X_SCOPE_CHILDREN:
1691         case LDAP_X_SCOPE_SUBTREE:
1692         case LDAP_X_SCOPE_ONELEVEL:
1693         {
1694                 int     d = assertDN->bv_len - op.o_req_ndn.bv_len;
1695
1696                 rc = LDAP_INAPPROPRIATE_AUTH;
1697
1698                 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1699                         goto exact_match;
1700
1701                 } else if ( d > 0 ) {
1702                         struct berval bv;
1703
1704                         /* leave room for at least one char of attributeType,
1705                          * one for '=' and one for ',' */
1706                         if ( d < STRLENOF( "x=,") ) {
1707                                 goto CONCLUDED;
1708                         }
1709
1710                         bv.bv_len = op.o_req_ndn.bv_len;
1711                         bv.bv_val = assertDN->bv_val + d;
1712
1713                         if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1714                                 switch ( op.ors_scope ) {
1715                                 case LDAP_X_SCOPE_SUBTREE:
1716                                 case LDAP_X_SCOPE_CHILDREN:
1717                                         rc = LDAP_SUCCESS;
1718                                         break;
1719
1720                                 case LDAP_X_SCOPE_ONELEVEL:
1721                                 {
1722                                         struct berval   pdn;
1723
1724                                         dnParent( assertDN, &pdn );
1725                                         /* the common portion of the DN
1726                                          * already matches, so only check
1727                                          * if parent DN of assertedDN 
1728                                          * is all the pattern */
1729                                         if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1730                                                 rc = LDAP_SUCCESS;
1731                                         }
1732                                         break;
1733                                 }
1734                                 default:
1735                                         /* at present, impossible */
1736                                         assert( 0 );
1737                                 }
1738                         }
1739                 }
1740                 goto CONCLUDED;
1741         }
1742
1743         case LDAP_X_SCOPE_REGEX:
1744                 rc = regcomp(&reg, op.o_req_ndn.bv_val,
1745                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
1746                 if ( rc == 0 ) {
1747                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
1748                         regfree( &reg );
1749                 }
1750                 if ( rc == 0 ) {
1751                         rc = LDAP_SUCCESS;
1752                 } else {
1753                         rc = LDAP_INAPPROPRIATE_AUTH;
1754                 }
1755                 goto CONCLUDED;
1756
1757         case LDAP_X_SCOPE_GROUP: {
1758                 char    *tmp;
1759
1760                 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1761                  * we need to append the <assertDN> so that the <group_dn> is searched
1762                  * with scope "base", and the filter ensures that <assertDN> is
1763                  * member of the group */
1764                 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1765                         assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1766                 if ( tmp == NULL ) {
1767                         rc = LDAP_NO_MEMORY;
1768                         goto CONCLUDED;
1769                 }
1770                 op.ors_filterstr.bv_val = tmp;
1771                 
1772                 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1773                 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1774
1775                 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1776                 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1777                 if ( op.ors_filter == NULL ) {
1778                         rc = LDAP_PROTOCOL_ERROR;
1779                         goto CONCLUDED;
1780                 }
1781                 op.ors_scope = LDAP_SCOPE_BASE;
1782
1783                 /* hijack match DN: use that of the group instead of the assertDN;
1784                  * assertDN is now in the filter */
1785                 sm.dn = &op.o_req_ndn;
1786
1787                 /* do the search */
1788                 break;
1789                 }
1790
1791         case LDAP_X_SCOPE_USERS:
1792                 if ( !BER_BVISEMPTY( assertDN ) ) {
1793                         rc = LDAP_SUCCESS;
1794                 } else {
1795                         rc = LDAP_INAPPROPRIATE_AUTH;
1796                 }
1797                 goto CONCLUDED;
1798
1799         default:
1800                 break;
1801         }
1802
1803         /* Must run an internal search. */
1804         if ( op.ors_filter == NULL ) {
1805                 rc = LDAP_FILTER_ERROR;
1806                 goto CONCLUDED;
1807         }
1808
1809         Debug( LDAP_DEBUG_TRACE,
1810            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1811            op.o_req_ndn.bv_val, op.ors_scope, 0 );
1812
1813         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1814         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1815                 rc = LDAP_INAPPROPRIATE_AUTH;
1816                 goto CONCLUDED;
1817         }
1818
1819         op.o_hdr = opx->o_hdr;
1820         op.o_tag = LDAP_REQ_SEARCH;
1821         op.o_ndn = *authc;
1822         op.o_callback = &cb;
1823         slap_op_time( &op.o_time, &op.o_tincr );
1824         op.o_do_not_cache = 1;
1825         op.o_is_auth_check = 1;
1826         /* use req_ndn as req_dn instead of non-pretty base of uri */
1827         if( !BER_BVISNULL( &base ) ) {
1828                 ch_free( base.bv_val );
1829                 /* just in case... */
1830                 BER_BVZERO( &base );
1831         }
1832         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1833         op.ors_deref = LDAP_DEREF_NEVER;
1834         op.ors_slimit = 1;
1835         op.ors_tlimit = SLAP_NO_LIMIT;
1836         op.ors_attrs = slap_anlist_no_attrs;
1837         op.ors_attrsonly = 1;
1838
1839         op.o_bd->be_search( &op, &rs );
1840
1841         if (sm.match) {
1842                 rc = LDAP_SUCCESS;
1843         } else {
1844                 rc = LDAP_INAPPROPRIATE_AUTH;
1845         }
1846
1847 CONCLUDED:
1848         if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1849         if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1850         if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1851         if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1852
1853         Debug( LDAP_DEBUG_TRACE,
1854            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1855
1856         return( rc );
1857 }
1858
1859
1860 /*
1861  * This function answers the question, "Can this ID authorize to that ID?",
1862  * based on authorization rules. The rules are stored in the *searchDN, in the
1863  * attribute named by *attr. If any of those rules map to the *assertDN, the
1864  * authorization is approved.
1865  *
1866  * The DNs should not have the dn: prefix
1867  */
1868 static int
1869 slap_sasl_check_authz( Operation *op,
1870         struct berval *searchDN,
1871         struct berval *assertDN,
1872         AttributeDescription *ad,
1873         struct berval *authc )
1874 {
1875         int             rc,
1876                         do_not_cache = op->o_do_not_cache;
1877         BerVarray       vals = NULL;
1878
1879         Debug( LDAP_DEBUG_TRACE,
1880            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1881            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1882
1883         /* ITS#4760: don't cache group access */
1884         op->o_do_not_cache = 1;
1885         rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1886         op->o_do_not_cache = do_not_cache;
1887         if( rc != LDAP_SUCCESS ) goto COMPLETE;
1888
1889         /* Check if the *assertDN matches any *vals */
1890         rc = slap_sasl_matches( op, vals, assertDN, authc );
1891
1892 COMPLETE:
1893         if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1894
1895         Debug( LDAP_DEBUG_TRACE,
1896            "<==slap_sasl_check_authz: %s check returning %d\n",
1897                 ad->ad_cname.bv_val, rc, 0);
1898
1899         return( rc );
1900 }
1901
1902 /*
1903  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1904  * return the LDAP DN to which it matches. The SASL regexp rules in the config
1905  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1906  * search with scope=base), just return the URI (or its searchbase). Otherwise
1907  * an internal search must be done, and if that search returns exactly one
1908  * entry, return the DN of that one entry.
1909  */
1910 void
1911 slap_sasl2dn(
1912         Operation       *opx,
1913         struct berval   *saslname,
1914         struct berval   *sasldn,
1915         int             flags )
1916 {
1917         int rc;
1918         slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1919         Operation op = {0};
1920         SlapReply rs = {REP_RESULT};
1921         struct berval regout = BER_BVNULL;
1922         struct berval base = BER_BVNULL;
1923
1924         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1925                 "converting SASL name %s to a DN\n",
1926                 saslname->bv_val, 0,0 );
1927
1928         BER_BVZERO( sasldn );
1929         cb.sc_private = sasldn;
1930
1931         /* Convert the SASL name into a minimal URI */
1932         if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
1933                 goto FINISHED;
1934         }
1935
1936         /* NOTE: always normalize regout because it results
1937          * from string submatch expansion */
1938         rc = slap_parseURI( opx, &regout, &base, &op.o_req_ndn,
1939                 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1940         if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1941         if ( rc != LDAP_SUCCESS ) {
1942                 goto FINISHED;
1943         }
1944
1945         /* Must do an internal search */
1946         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1947
1948         switch ( op.ors_scope ) {
1949         case LDAP_X_SCOPE_EXACT:
1950                 *sasldn = op.o_req_ndn;
1951                 BER_BVZERO( &op.o_req_ndn );
1952                 /* intentionally continue to next case */
1953
1954         case LDAP_X_SCOPE_REGEX:
1955         case LDAP_X_SCOPE_SUBTREE:
1956         case LDAP_X_SCOPE_CHILDREN:
1957         case LDAP_X_SCOPE_ONELEVEL:
1958         case LDAP_X_SCOPE_GROUP:
1959         case LDAP_X_SCOPE_USERS:
1960                 /* correctly parsed, but illegal */
1961                 goto FINISHED;
1962
1963         case LDAP_SCOPE_BASE:
1964         case LDAP_SCOPE_ONELEVEL:
1965         case LDAP_SCOPE_SUBTREE:
1966         case LDAP_SCOPE_SUBORDINATE:
1967                 /* do a search */
1968                 break;
1969
1970         default:
1971                 /* catch unhandled cases (there shouldn't be) */
1972                 assert( 0 );
1973         }
1974
1975         Debug( LDAP_DEBUG_TRACE,
1976                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1977                 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1978
1979         if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1980                 goto FINISHED;
1981         }
1982
1983         /* Must run an internal search. */
1984         if ( op.ors_filter == NULL ) {
1985                 rc = LDAP_FILTER_ERROR;
1986                 goto FINISHED;
1987         }
1988
1989         op.o_hdr = opx->o_hdr;
1990         op.o_tag = LDAP_REQ_SEARCH;
1991         op.o_ndn = opx->o_conn->c_ndn;
1992         op.o_callback = &cb;
1993         slap_op_time( &op.o_time, &op.o_tincr );
1994         op.o_do_not_cache = 1;
1995         op.o_is_auth_check = 1;
1996         op.ors_deref = LDAP_DEREF_NEVER;
1997         op.ors_slimit = 1;
1998         op.ors_tlimit = SLAP_NO_LIMIT;
1999         op.ors_attrs = slap_anlist_no_attrs;
2000         op.ors_attrsonly = 1;
2001         /* use req_ndn as req_dn instead of non-pretty base of uri */
2002         if( !BER_BVISNULL( &base ) ) {
2003                 ch_free( base.bv_val );
2004                 /* just in case... */
2005                 BER_BVZERO( &base );
2006         }
2007         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2008
2009         op.o_bd->be_search( &op, &rs );
2010         
2011 FINISHED:
2012         if( !BER_BVISEMPTY( sasldn ) ) {
2013                 opx->o_conn->c_authz_backend = op.o_bd;
2014         }
2015         if( !BER_BVISNULL( &op.o_req_dn ) ) {
2016                 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2017         }
2018         if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2019                 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2020         }
2021         if( op.ors_filter ) {
2022                 filter_free_x( opx, op.ors_filter );
2023         }
2024         if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2025                 ch_free( op.ors_filterstr.bv_val );
2026         }
2027
2028         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2029                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2030
2031         return;
2032 }
2033
2034
2035 /* Check if a bind can SASL authorize to another identity.
2036  * The DNs should not have the dn: prefix
2037  */
2038
2039 int slap_sasl_authorized( Operation *op,
2040         struct berval *authcDN, struct berval *authzDN )
2041 {
2042         int rc = LDAP_INAPPROPRIATE_AUTH;
2043
2044         /* User binding as anonymous */
2045         if ( authzDN == NULL ) {
2046                 rc = LDAP_SUCCESS;
2047                 goto DONE;
2048         }
2049
2050         Debug( LDAP_DEBUG_TRACE,
2051            "==>slap_sasl_authorized: can %s become %s?\n",
2052                 authcDN->bv_len ? authcDN->bv_val : "(null)",
2053                 authzDN->bv_len ? authzDN->bv_val : "(null)",  0 );
2054
2055         /* If person is authorizing to self, succeed */
2056         if ( dn_match( authcDN, authzDN ) ) {
2057                 rc = LDAP_SUCCESS;
2058                 goto DONE;
2059         }
2060
2061         /* Allow the manager to authorize as any DN. */
2062         if( op->o_conn->c_authz_backend &&
2063                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2064         {
2065                 rc = LDAP_SUCCESS;
2066                 goto DONE;
2067         }
2068
2069         /* Check source rules */
2070         if( authz_policy & SASL_AUTHZ_TO ) {
2071                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2072                         slap_schema.si_ad_saslAuthzTo, authcDN );
2073                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2074                         goto DONE;
2075                 }
2076         }
2077
2078         /* Check destination rules */
2079         if( authz_policy & SASL_AUTHZ_FROM ) {
2080                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2081                         slap_schema.si_ad_saslAuthzFrom, authcDN );
2082                 if( rc == LDAP_SUCCESS ) {
2083                         goto DONE;
2084                 }
2085         }
2086
2087         rc = LDAP_INAPPROPRIATE_AUTH;
2088
2089 DONE:
2090
2091         Debug( LDAP_DEBUG_TRACE,
2092                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
2093
2094         return( rc );
2095 }