]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
ITS#4358
[openldap] / servers / slapd / saslauthz.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2006 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                 }
1140                 group_dn.bv_val++;
1141                 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1142
1143                 if ( normalize ) {
1144                         rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1145                         if ( rc != LDAP_SUCCESS ) {
1146                                 *scope = -1;
1147                                 return rc;
1148                         }
1149                 } else {
1150                         ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1151                         rc = LDAP_SUCCESS;
1152                 }
1153                 *scope = LDAP_X_SCOPE_GROUP;
1154
1155                 /* FIXME: caller needs to add value of member attribute
1156                  * and close brackets twice */
1157                 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
1158                         + group_oc.bv_len + member_at.bv_len;
1159                 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
1160
1161                 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
1162                                 STRLENOF( "(&(objectClass=" /* )) */ ) );
1163                 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
1164                 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
1165                                 STRLENOF( /* ( */ ")(" /* ) */ ) );
1166                 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
1167                 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
1168
1169                 return rc;
1170         }
1171
1172         /*
1173          * ldap:///<base>??<scope>?<filter>
1174          * <scope> ::= {base|one|subtree}
1175          *
1176          * <scope> defaults to "base"
1177          * <base> must pass DN normalization
1178          * <filter> must pass str2filter()
1179          */
1180         rc = ldap_url_parse( uri->bv_val, &ludp );
1181         switch ( rc ) {
1182         case LDAP_URL_SUCCESS:
1183                 /* FIXME: the check is pedantic, but I think it's necessary,
1184                  * because people tend to use things like ldaps:// which
1185                  * gives the idea SSL is being used.  Maybe we could
1186                  * accept ldapi:// as well, but the point is that we use
1187                  * an URL as an easy means to define bits of a search with
1188                  * little parsing.
1189                  */
1190                 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1191                         /*
1192                          * must be ldap:///
1193                          */
1194                         rc = LDAP_PROTOCOL_ERROR;
1195                         goto done;
1196                 }
1197                 break;
1198
1199         case LDAP_URL_ERR_BADSCHEME:
1200                 /*
1201                  * last chance: assume it's a(n exact) DN ...
1202                  *
1203                  * NOTE: must pass DN normalization
1204                  */
1205                 ldap_free_urldesc( ludp );
1206                 bv.bv_val = uri->bv_val;
1207                 *scope = LDAP_X_SCOPE_EXACT;
1208                 goto is_dn;
1209
1210         default:
1211                 rc = LDAP_PROTOCOL_ERROR;
1212                 goto done;
1213         }
1214
1215         if ( ( ludp->lud_host && *ludp->lud_host )
1216                 || ludp->lud_attrs || ludp->lud_exts )
1217         {
1218                 /* host part must be empty */
1219                 /* attrs and extensions parts must be empty */
1220                 rc = LDAP_PROTOCOL_ERROR;
1221                 goto done;
1222         }
1223
1224         /* Grab the scope */
1225         *scope = ludp->lud_scope;
1226
1227         /* Grab the filter */
1228         if ( ludp->lud_filter ) {
1229                 *filter = str2filter_x( op, ludp->lud_filter );
1230                 if ( *filter == NULL ) {
1231                         rc = LDAP_PROTOCOL_ERROR;
1232                         goto done;
1233                 }
1234                 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1235         }
1236
1237         /* Grab the searchbase */
1238         ber_str2bv( ludp->lud_dn, 0, 0, base );
1239         if ( normalize ) {
1240                 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1241         } else {
1242                 ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1243                 rc = LDAP_SUCCESS;
1244         }
1245
1246 done:
1247         if( rc != LDAP_SUCCESS ) {
1248                 if( *filter ) filter_free_x( op, *filter );
1249                 BER_BVZERO( base );
1250                 BER_BVZERO( fstr );
1251         } else {
1252                 /* Don't free these, return them to caller */
1253                 ludp->lud_filter = NULL;
1254                 ludp->lud_dn = NULL;
1255         }
1256
1257         ldap_free_urldesc( ludp );
1258         return( rc );
1259 }
1260
1261 #ifndef SLAP_AUTH_REWRITE
1262 static int slap_sasl_rx_off(char *rep, int *off)
1263 {
1264         const char *c;
1265         int n;
1266
1267         /* Precompile replace pattern. Find the $<n> placeholders */
1268         off[0] = -2;
1269         n = 1;
1270         for ( c = rep;   *c;  c++ ) {
1271                 if ( *c == '\\' && c[1] ) {
1272                         c++;
1273                         continue;
1274                 }
1275                 if ( *c == '$' ) {
1276                         if ( n == SASLREGEX_REPLACE ) {
1277                                 Debug( LDAP_DEBUG_ANY,
1278                                         "SASL replace pattern %s has too many $n "
1279                                                 "placeholders (max %d)\n",
1280                                         rep, SASLREGEX_REPLACE, 0 );
1281
1282                                 return( LDAP_OTHER );
1283                         }
1284                         off[n] = c - rep;
1285                         n++;
1286                 }
1287         }
1288
1289         /* Final placeholder, after the last $n */
1290         off[n] = c - rep;
1291         n++;
1292         off[n] = -1;
1293         return( LDAP_SUCCESS );
1294 }
1295 #endif /* ! SLAP_AUTH_REWRITE */
1296
1297 #ifdef SLAP_AUTH_REWRITE
1298 int slap_sasl_rewrite_config( 
1299                 const char      *fname,
1300                 int             lineno,
1301                 int             argc,
1302                 char            **argv
1303 )
1304 {
1305         int     rc;
1306         char    *savearg0;
1307
1308         /* init at first call */
1309         if ( sasl_rwinfo == NULL ) {
1310                 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1311         }
1312
1313         /* strip "authid-" prefix for parsing */
1314         savearg0 = argv[0];
1315         argv[0] += STRLENOF( "authid-" );
1316         rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1317         argv[0] = savearg0;
1318
1319         return rc;
1320 }
1321
1322 int slap_sasl_rewrite_destroy( void )
1323 {
1324         if ( sasl_rwinfo ) {
1325                 rewrite_info_delete( &sasl_rwinfo );
1326                 sasl_rwinfo = NULL;
1327         }
1328
1329         return 0;
1330 }
1331
1332 int slap_sasl_regexp_rewrite_config(
1333                 const char      *fname,
1334                 int             lineno,
1335                 const char      *match,
1336                 const char      *replace,
1337                 const char      *context )
1338 {
1339         int     rc;
1340         char    *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1341
1342         /* init at first call */
1343         if ( sasl_rwinfo == NULL ) {
1344                 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1345                 char *argvContext[] = { "rewriteContext", NULL, NULL };
1346
1347                 /* initialize rewrite engine */
1348                 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1349
1350                 /* switch on rewrite engine */
1351                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1352                 if (rc != LDAP_SUCCESS) {
1353                         return rc;
1354                 }
1355
1356                 /* create generic authid context */
1357                 argvContext[1] = AUTHID_CONTEXT;
1358                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1359                 if (rc != LDAP_SUCCESS) {
1360                         return rc;
1361                 }
1362         }
1363
1364         argvRule[1] = (char *)match;
1365         argvRule[2] = (char *)replace;
1366         rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1367
1368         return rc;
1369 }
1370 #endif /* SLAP_AUTH_REWRITE */
1371
1372 int slap_sasl_regexp_config( const char *match, const char *replace )
1373 {
1374         int rc;
1375         SaslRegexp_t *reg;
1376
1377         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1378           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1379
1380         reg = &SaslRegexp[nSaslRegexp];
1381
1382         reg->sr_match = ch_strdup( match );
1383         reg->sr_replace = ch_strdup( replace );
1384
1385 #ifdef SLAP_AUTH_REWRITE
1386         rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1387                         match, replace, AUTHID_CONTEXT );
1388         if ( rc == LDAP_SUCCESS ) nSaslRegexp++;
1389         return rc;
1390 #else /* ! SLAP_AUTH_REWRITE */
1391
1392         /* Precompile matching pattern */
1393         rc = regcomp( &reg->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
1394         if ( rc ) {
1395                 Debug( LDAP_DEBUG_ANY,
1396                 "SASL match pattern %s could not be compiled by regexp engine\n",
1397                 reg->sr_match, 0, 0 );
1398
1399 #ifdef ENABLE_REWRITE
1400         /* Dummy block to force symbol references in librewrite */
1401         if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1402                 rewrite_info_init( 0 );
1403         }
1404 #endif
1405                 return( LDAP_OTHER );
1406         }
1407
1408         rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
1409         if ( rc != LDAP_SUCCESS ) return rc;
1410
1411         nSaslRegexp++;
1412         return( LDAP_SUCCESS );
1413 #endif /* ! SLAP_AUTH_REWRITE */
1414 }
1415
1416 void slap_sasl_regexp_unparse( BerVarray *out )
1417 {
1418         int i;
1419         BerVarray bva = NULL;
1420         char ibuf[32], *ptr;
1421         struct berval idx;
1422
1423         if ( !nSaslRegexp ) return;
1424
1425         idx.bv_val = ibuf;
1426         bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1427         BER_BVZERO(bva+nSaslRegexp);
1428         for ( i=0; i<nSaslRegexp; i++ ) {
1429                 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1430                 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1431                         strlen( SaslRegexp[i].sr_replace ) + 5;
1432                 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1433                 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1434                 *ptr++ = '"';
1435                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1436                 ptr = lutil_strcopy( ptr, "\" \"" );
1437                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1438                 *ptr++ = '"';
1439                 *ptr = '\0';
1440         }
1441         *out = bva;
1442 }
1443
1444 #ifndef SLAP_AUTH_REWRITE
1445 /* Perform replacement on regexp matches */
1446 static void slap_sasl_rx_exp(
1447         const char *rep,
1448         const int *off,
1449         regmatch_t *str,
1450         const char *saslname,
1451         struct berval *out,
1452         void *ctx )
1453 {
1454         int i, n, len, insert;
1455
1456         /* Get the total length of the final URI */
1457
1458         n=1;
1459         len = 0;
1460         while( off[n] >= 0 ) {
1461                 /* Len of next section from replacement string (x,y,z above) */
1462                 len += off[n] - off[n-1] - 2;
1463                 if( off[n+1] < 0)
1464                         break;
1465
1466                 /* Len of string from saslname that matched next $i  (b,d above) */
1467                 i = rep[ off[n] + 1 ]   - '0';
1468                 len += str[i].rm_eo - str[i].rm_so;
1469                 n++;
1470         }
1471         out->bv_val = slap_sl_malloc( len + 1, ctx );
1472         out->bv_len = len;
1473
1474         /* Fill in URI with replace string, replacing $i as we go */
1475         n=1;
1476         insert = 0;
1477         while( off[n] >= 0) {
1478                 /* Paste in next section from replacement string (x,y,z above) */
1479                 len = off[n] - off[n-1] - 2;
1480                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1481                 insert += len;
1482                 if( off[n+1] < 0)
1483                         break;
1484
1485                 /* Paste in string from saslname that matched next $i  (b,d above) */
1486                 i = rep[ off[n] + 1 ]   - '0';
1487                 len = str[i].rm_eo - str[i].rm_so;
1488                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1489                 insert += len;
1490
1491                 n++;
1492         }
1493
1494         out->bv_val[insert] = '\0';
1495 }
1496 #endif /* ! SLAP_AUTH_REWRITE */
1497
1498 /* Take the passed in SASL name and attempt to convert it into an
1499    LDAP URI to find the matching LDAP entry, using the pattern matching
1500    strings given in the saslregexp config file directive(s) */
1501
1502 static int slap_authz_regexp( struct berval *in, struct berval *out,
1503                 int flags, void *ctx )
1504 {
1505 #ifdef SLAP_AUTH_REWRITE
1506         const char      *context = AUTHID_CONTEXT;
1507
1508         if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1509                 return 0;
1510         }
1511
1512         /* FIXME: if aware of authc/authz mapping, 
1513          * we could use different contexts ... */
1514         switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 
1515                                 &out->bv_val ) )
1516         {
1517         case REWRITE_REGEXEC_OK:
1518                 if ( !BER_BVISNULL( out ) ) {
1519                         char *val = out->bv_val;
1520                         ber_str2bv_x( val, 0, 1, out, ctx );
1521                         if ( val != in->bv_val ) {
1522                                 free( val );
1523                         }
1524                 } else {
1525                         ber_dupbv_x( out, in, ctx );
1526                 }
1527                 Debug( LDAP_DEBUG_ARGS,
1528                         "[rw] %s: \"%s\" -> \"%s\"\n",
1529                         context, in->bv_val, out->bv_val );             
1530                 return 1;
1531                 
1532         case REWRITE_REGEXEC_UNWILLING:
1533         case REWRITE_REGEXEC_ERR:
1534         default:
1535                 return 0;
1536         }
1537
1538 #else /* ! SLAP_AUTH_REWRITE */
1539         char *saslname = in->bv_val;
1540         SaslRegexp_t *reg;
1541         regmatch_t sr_strings[SASLREGEX_REPLACE];       /* strings matching $1,$2 ... */
1542         int i;
1543
1544         memset( out, 0, sizeof( *out ) );
1545
1546         Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1547            saslname, 0, 0 );
1548
1549         if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1550                 return( 0 );
1551         }
1552
1553         /* Match the normalized SASL name to the saslregexp patterns */
1554         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
1555                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
1556                   sr_strings, 0)  == 0 )
1557                         break;
1558         }
1559
1560         if( i >= nSaslRegexp ) return( 0 );
1561
1562         /*
1563          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1564          * replace pattern of the form "x$1y$2z". The returned string needs
1565          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1566          */
1567         slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1568                 sr_strings, saslname, out, ctx );
1569
1570         Debug( LDAP_DEBUG_TRACE,
1571                 "slap_authz_regexp: converted SASL name to %s\n",
1572                 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1573
1574         return( 1 );
1575 #endif /* ! SLAP_AUTH_REWRITE */
1576 }
1577
1578 /* This callback actually does some work...*/
1579 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1580 {
1581         struct berval *ndn = op->o_callback->sc_private;
1582
1583         if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1584
1585         /* We only want to be called once */
1586         if ( !BER_BVISNULL( ndn ) ) {
1587                 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1588                 BER_BVZERO( ndn );
1589
1590                 Debug( LDAP_DEBUG_TRACE,
1591                         "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1592                         op->o_log_prefix, 0, 0 );
1593                 return LDAP_OTHER;
1594         }
1595
1596         ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1597         return LDAP_SUCCESS;
1598 }
1599
1600
1601 typedef struct smatch_info {
1602         struct berval *dn;
1603         int match;
1604 } smatch_info;
1605
1606 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1607 {
1608         smatch_info *sm = o->o_callback->sc_private;
1609
1610         if ( rs->sr_type != REP_SEARCH ) {
1611                 if ( rs->sr_err != LDAP_SUCCESS ) {
1612                         sm->match = -1;
1613                 }
1614                 return 0;
1615         }
1616
1617         if ( sm->match == 1 ) {
1618                 sm->match = -1;
1619                 return 0;
1620         }
1621
1622         if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1623                 sm->match = 1;
1624
1625         } else {
1626                 sm->match = -1;
1627         }
1628
1629         return 0;
1630 }
1631
1632 int
1633 slap_sasl_matches( Operation *op, BerVarray rules,
1634                 struct berval *assertDN, struct berval *authc )
1635 {
1636         int     rc = LDAP_INAPPROPRIATE_AUTH;
1637
1638         if ( rules != NULL ) {
1639                 int     i;
1640
1641                 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1642                         rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1643                         if ( rc == LDAP_SUCCESS ) break;
1644                 }
1645         }
1646         
1647         return rc;
1648 }
1649
1650 /*
1651  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1652  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1653  * the rule must be used as an internal search for entries. If that search
1654  * returns the *assertDN entry, the match is successful.
1655  *
1656  * The assertDN should not have the dn: prefix
1657  */
1658
1659 static int
1660 slap_sasl_match( Operation *opx, struct berval *rule,
1661         struct berval *assertDN, struct berval *authc )
1662 {
1663         int rc; 
1664         regex_t reg;
1665         smatch_info sm;
1666         slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1667         Operation op = {0};
1668         SlapReply rs = {REP_RESULT};
1669         struct berval base = BER_BVNULL;
1670
1671         sm.dn = assertDN;
1672         sm.match = 0;
1673         cb.sc_private = &sm;
1674
1675         Debug( LDAP_DEBUG_TRACE,
1676            "===>slap_sasl_match: comparing DN %s to rule %s\n",
1677                 assertDN->bv_val, rule->bv_val, 0 );
1678
1679         /* NOTE: don't normalize rule if authz syntax is enabled */
1680         rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1681                 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 
1682 #ifdef SLAP_AUTHZ_SYNTAX
1683                 0
1684 #else /* ! SLAP_AUTHZ_SYNTAX */
1685                 1
1686 #endif /* ! SLAP_AUTHZ_SYNTAX */
1687                 );
1688
1689         if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1690
1691         switch ( op.ors_scope ) {
1692         case LDAP_X_SCOPE_EXACT:
1693 exact_match:
1694                 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1695                         rc = LDAP_SUCCESS;
1696                 } else {
1697                         rc = LDAP_INAPPROPRIATE_AUTH;
1698                 }
1699                 goto CONCLUDED;
1700
1701         case LDAP_X_SCOPE_CHILDREN:
1702         case LDAP_X_SCOPE_SUBTREE:
1703         case LDAP_X_SCOPE_ONELEVEL:
1704         {
1705                 int     d = assertDN->bv_len - op.o_req_ndn.bv_len;
1706
1707                 rc = LDAP_INAPPROPRIATE_AUTH;
1708
1709                 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1710                         goto exact_match;
1711
1712                 } else if ( d > 0 ) {
1713                         struct berval bv;
1714
1715                         /* leave room for at least one char of attributeType,
1716                          * one for '=' and one for ',' */
1717                         if ( d < STRLENOF( "x=,") ) {
1718                                 goto CONCLUDED;
1719                         }
1720
1721                         bv.bv_len = op.o_req_ndn.bv_len;
1722                         bv.bv_val = assertDN->bv_val + d;
1723
1724                         if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1725                                 switch ( op.ors_scope ) {
1726                                 case LDAP_X_SCOPE_SUBTREE:
1727                                 case LDAP_X_SCOPE_CHILDREN:
1728                                         rc = LDAP_SUCCESS;
1729                                         break;
1730
1731                                 case LDAP_X_SCOPE_ONELEVEL:
1732                                 {
1733                                         struct berval   pdn;
1734
1735                                         dnParent( assertDN, &pdn );
1736                                         /* the common portion of the DN
1737                                          * already matches, so only check
1738                                          * if parent DN of assertedDN 
1739                                          * is all the pattern */
1740                                         if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1741                                                 rc = LDAP_SUCCESS;
1742                                         }
1743                                         break;
1744                                 }
1745                                 default:
1746                                         /* at present, impossible */
1747                                         assert( 0 );
1748                                 }
1749                         }
1750                 }
1751                 goto CONCLUDED;
1752         }
1753
1754         case LDAP_X_SCOPE_REGEX:
1755                 rc = regcomp(&reg, op.o_req_ndn.bv_val,
1756                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
1757                 if ( rc == 0 ) {
1758                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
1759                         regfree( &reg );
1760                 }
1761                 if ( rc == 0 ) {
1762                         rc = LDAP_SUCCESS;
1763                 } else {
1764                         rc = LDAP_INAPPROPRIATE_AUTH;
1765                 }
1766                 goto CONCLUDED;
1767
1768         case LDAP_X_SCOPE_GROUP: {
1769                 char    *tmp;
1770
1771                 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1772                  * we need to append the <assertDN> so that the <group_dn> is searched
1773                  * with scope "base", and the filter ensures that <assertDN> is
1774                  * member of the group */
1775                 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1776                         assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1777                 if ( tmp == NULL ) {
1778                         rc = LDAP_NO_MEMORY;
1779                         goto CONCLUDED;
1780                 }
1781                 op.ors_filterstr.bv_val = tmp;
1782                 
1783                 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1784                 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1785
1786                 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1787                 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1788                 if ( op.ors_filter == NULL ) {
1789                         rc = LDAP_PROTOCOL_ERROR;
1790                         goto CONCLUDED;
1791                 }
1792                 op.ors_scope = LDAP_SCOPE_BASE;
1793
1794                 /* hijack match DN: use that of the group instead of the assertDN;
1795                  * assertDN is now in the filter */
1796                 sm.dn = &op.o_req_ndn;
1797
1798                 /* do the search */
1799                 break;
1800                 }
1801
1802         case LDAP_X_SCOPE_USERS:
1803                 if ( !BER_BVISEMPTY( assertDN ) ) {
1804                         rc = LDAP_SUCCESS;
1805                 } else {
1806                         rc = LDAP_INAPPROPRIATE_AUTH;
1807                 }
1808                 goto CONCLUDED;
1809
1810         default:
1811                 break;
1812         }
1813
1814         /* Must run an internal search. */
1815         if ( op.ors_filter == NULL ) {
1816                 rc = LDAP_FILTER_ERROR;
1817                 goto CONCLUDED;
1818         }
1819
1820         Debug( LDAP_DEBUG_TRACE,
1821            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1822            op.o_req_ndn.bv_val, op.ors_scope, 0 );
1823
1824         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1825         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1826                 rc = LDAP_INAPPROPRIATE_AUTH;
1827                 goto CONCLUDED;
1828         }
1829
1830         op.o_hdr = opx->o_hdr;
1831         op.o_tag = LDAP_REQ_SEARCH;
1832         op.o_ndn = *authc;
1833         op.o_callback = &cb;
1834         slap_op_time( &op.o_time, &op.o_tincr );
1835         op.o_do_not_cache = 1;
1836         op.o_is_auth_check = 1;
1837         /* use req_ndn as req_dn instead of non-pretty base of uri */
1838         if( !BER_BVISNULL( &base ) ) {
1839                 ch_free( base.bv_val );
1840                 /* just in case... */
1841                 BER_BVZERO( &base );
1842         }
1843         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1844         op.ors_deref = LDAP_DEREF_NEVER;
1845         op.ors_slimit = 1;
1846         op.ors_tlimit = SLAP_NO_LIMIT;
1847         op.ors_attrs = slap_anlist_no_attrs;
1848         op.ors_attrsonly = 1;
1849
1850         op.o_bd->be_search( &op, &rs );
1851
1852         if (sm.match == 1) {
1853                 rc = LDAP_SUCCESS;
1854         } else {
1855                 rc = LDAP_INAPPROPRIATE_AUTH;
1856         }
1857
1858 CONCLUDED:
1859         if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1860         if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1861         if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1862         if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1863
1864         Debug( LDAP_DEBUG_TRACE,
1865            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1866
1867         return( rc );
1868 }
1869
1870
1871 /*
1872  * This function answers the question, "Can this ID authorize to that ID?",
1873  * based on authorization rules. The rules are stored in the *searchDN, in the
1874  * attribute named by *attr. If any of those rules map to the *assertDN, the
1875  * authorization is approved.
1876  *
1877  * The DNs should not have the dn: prefix
1878  */
1879 static int
1880 slap_sasl_check_authz( Operation *op,
1881         struct berval *searchDN,
1882         struct berval *assertDN,
1883         AttributeDescription *ad,
1884         struct berval *authc )
1885 {
1886         int rc;
1887         BerVarray vals = NULL;
1888
1889         Debug( LDAP_DEBUG_TRACE,
1890            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1891            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1892
1893         rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1894         if( rc != LDAP_SUCCESS ) goto COMPLETE;
1895
1896         /* Check if the *assertDN matches any *vals */
1897         rc = slap_sasl_matches( op, vals, assertDN, authc );
1898
1899 COMPLETE:
1900         if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1901
1902         Debug( LDAP_DEBUG_TRACE,
1903            "<==slap_sasl_check_authz: %s check returning %d\n",
1904                 ad->ad_cname.bv_val, rc, 0);
1905
1906         return( rc );
1907 }
1908
1909 /*
1910  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1911  * return the LDAP DN to which it matches. The SASL regexp rules in the config
1912  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1913  * search with scope=base), just return the URI (or its searchbase). Otherwise
1914  * an internal search must be done, and if that search returns exactly one
1915  * entry, return the DN of that one entry.
1916  */
1917 void
1918 slap_sasl2dn(
1919         Operation       *opx,
1920         struct berval   *saslname,
1921         struct berval   *sasldn,
1922         int             flags )
1923 {
1924         int rc;
1925         slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1926         Operation op = {0};
1927         SlapReply rs = {REP_RESULT};
1928         struct berval regout = BER_BVNULL;
1929         struct berval base = BER_BVNULL;
1930
1931         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1932                 "converting SASL name %s to a DN\n",
1933                 saslname->bv_val, 0,0 );
1934
1935         BER_BVZERO( sasldn );
1936         cb.sc_private = sasldn;
1937
1938         /* Convert the SASL name into a minimal URI */
1939         if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
1940                 goto FINISHED;
1941         }
1942
1943         /* NOTE: always normalize regout because it results
1944          * from string submatch expansion */
1945         rc = slap_parseURI( opx, &regout, &base, &op.o_req_ndn,
1946                 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1947         if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1948         if ( rc != LDAP_SUCCESS ) {
1949                 goto FINISHED;
1950         }
1951
1952         /* Must do an internal search */
1953         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1954
1955         switch ( op.ors_scope ) {
1956         case LDAP_X_SCOPE_EXACT:
1957                 *sasldn = op.o_req_ndn;
1958                 BER_BVZERO( &op.o_req_ndn );
1959                 /* intentionally continue to next case */
1960
1961         case LDAP_X_SCOPE_REGEX:
1962         case LDAP_X_SCOPE_SUBTREE:
1963         case LDAP_X_SCOPE_CHILDREN:
1964         case LDAP_X_SCOPE_ONELEVEL:
1965         case LDAP_X_SCOPE_GROUP:
1966         case LDAP_X_SCOPE_USERS:
1967                 /* correctly parsed, but illegal */
1968                 goto FINISHED;
1969
1970         case LDAP_SCOPE_BASE:
1971         case LDAP_SCOPE_ONELEVEL:
1972         case LDAP_SCOPE_SUBTREE:
1973         case LDAP_SCOPE_SUBORDINATE:
1974                 /* do a search */
1975                 break;
1976
1977         default:
1978                 /* catch unhandled cases (there shouldn't be) */
1979                 assert( 0 );
1980         }
1981
1982         Debug( LDAP_DEBUG_TRACE,
1983                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1984                 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1985
1986         if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1987                 goto FINISHED;
1988         }
1989
1990         /* Must run an internal search. */
1991         if ( op.ors_filter == NULL ) {
1992                 rc = LDAP_FILTER_ERROR;
1993                 goto FINISHED;
1994         }
1995
1996         op.o_hdr = opx->o_hdr;
1997         op.o_tag = LDAP_REQ_SEARCH;
1998         op.o_ndn = opx->o_conn->c_ndn;
1999         op.o_callback = &cb;
2000         slap_op_time( &op.o_time, &op.o_tincr );
2001         op.o_do_not_cache = 1;
2002         op.o_is_auth_check = 1;
2003         op.ors_deref = LDAP_DEREF_NEVER;
2004         op.ors_slimit = 1;
2005         op.ors_tlimit = SLAP_NO_LIMIT;
2006         op.ors_attrs = slap_anlist_no_attrs;
2007         op.ors_attrsonly = 1;
2008         /* use req_ndn as req_dn instead of non-pretty base of uri */
2009         if( !BER_BVISNULL( &base ) ) {
2010                 ch_free( base.bv_val );
2011                 /* just in case... */
2012                 BER_BVZERO( &base );
2013         }
2014         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2015
2016         op.o_bd->be_search( &op, &rs );
2017         
2018 FINISHED:
2019         if( !BER_BVISEMPTY( sasldn ) ) {
2020                 opx->o_conn->c_authz_backend = op.o_bd;
2021         }
2022         if( !BER_BVISNULL( &op.o_req_dn ) ) {
2023                 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2024         }
2025         if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2026                 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2027         }
2028         if( op.ors_filter ) {
2029                 filter_free_x( opx, op.ors_filter );
2030         }
2031         if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2032                 ch_free( op.ors_filterstr.bv_val );
2033         }
2034
2035         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2036                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2037
2038         return;
2039 }
2040
2041
2042 /* Check if a bind can SASL authorize to another identity.
2043  * The DNs should not have the dn: prefix
2044  */
2045
2046 int slap_sasl_authorized( Operation *op,
2047         struct berval *authcDN, struct berval *authzDN )
2048 {
2049         int rc = LDAP_INAPPROPRIATE_AUTH;
2050
2051         /* User binding as anonymous */
2052         if ( authzDN == NULL ) {
2053                 rc = LDAP_SUCCESS;
2054                 goto DONE;
2055         }
2056
2057         Debug( LDAP_DEBUG_TRACE,
2058            "==>slap_sasl_authorized: can %s become %s?\n",
2059                 authcDN->bv_len ? authcDN->bv_val : "(null)",
2060                 authzDN->bv_len ? authzDN->bv_val : "(null)",  0 );
2061
2062         /* If person is authorizing to self, succeed */
2063         if ( dn_match( authcDN, authzDN ) ) {
2064                 rc = LDAP_SUCCESS;
2065                 goto DONE;
2066         }
2067
2068         /* Allow the manager to authorize as any DN. */
2069         if( op->o_conn->c_authz_backend &&
2070                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2071         {
2072                 rc = LDAP_SUCCESS;
2073                 goto DONE;
2074         }
2075
2076         /* Check source rules */
2077         if( authz_policy & SASL_AUTHZ_TO ) {
2078                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2079                         slap_schema.si_ad_saslAuthzTo, authcDN );
2080                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2081                         goto DONE;
2082                 }
2083         }
2084
2085         /* Check destination rules */
2086         if( authz_policy & SASL_AUTHZ_FROM ) {
2087                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2088                         slap_schema.si_ad_saslAuthzFrom, authcDN );
2089                 if( rc == LDAP_SUCCESS ) {
2090                         goto DONE;
2091                 }
2092         }
2093
2094         rc = LDAP_INAPPROPRIATE_AUTH;
2095
2096 DONE:
2097
2098         Debug( LDAP_DEBUG_TRACE,
2099                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
2100
2101         return( rc );
2102 }