]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
Sync with HEAD
[openldap] / servers / slapd / saslauthz.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2005 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 = strchr( id->bv_val, ':' );
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 = strchr( id->bv_val, '.' );
160         if ( !BER_BVISNULL( mech ) ) {
161                 mech->bv_val[ 0 ] = '\0';
162                 mech->bv_val++;
163
164                 realm->bv_val = strchr( mech->bv_val, '/' );
165
166                 if ( !BER_BVISNULL( realm ) ) {
167                         realm->bv_val[ 0 ] = '\0';
168                         realm->bv_val++;
169                         mech->bv_len = realm->bv_val - mech->bv_val - 1;
170                         realm->bv_len = user->bv_val - realm->bv_val - 1;
171                 } else {
172                         mech->bv_len = user->bv_val - mech->bv_val - 1;
173                 }
174
175         } else {
176                 BER_BVZERO( realm );
177         }
178
179         if ( id->bv_val[ 1 ] != '\0' ) {
180                 return LDAP_PROTOCOL_ERROR;
181         }
182
183         if ( !BER_BVISNULL( mech ) ) {
184                 assert( mech->bv_val == id->bv_val + 2 );
185
186                 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
187                 mech->bv_val -= 2;
188         }
189
190         if ( !BER_BVISNULL( realm ) ) {
191                 assert( realm->bv_val >= id->bv_val + 2 );
192
193                 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
194                 realm->bv_val -= 2;
195         }
196
197         /* leave "u:" before user */
198         user->bv_val -= 2;
199         user->bv_len += 2;
200         user->bv_val[ 0 ] = u;
201         user->bv_val[ 1 ] = ':';
202
203         return LDAP_SUCCESS;
204 }
205
206 #ifdef SLAP_AUTHZ_SYNTAX
207 int
208 authzValidate(
209         Syntax *syntax,
210         struct berval *in )
211 {
212         struct berval   bv;
213         int             rc = LDAP_INVALID_SYNTAX;
214         LDAPURLDesc     *ludp = NULL;
215         int             scope = -1;
216
217         /*
218          * 1) <DN>
219          * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
220          * 3) dn.regex:<pattern>
221          * 4) u[.mech[/realm]]:<ID>
222          * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
223          * 6) <URL>
224          */
225
226         assert( in != NULL );
227         assert( !BER_BVISNULL( in ) );
228
229         Debug( LDAP_DEBUG_TRACE,
230                 "authzValidate: parsing %s\n", in->bv_val, 0, 0 );
231
232         /*
233          * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
234          * 3) dn.regex:<pattern>
235          *
236          * <DN> must pass DN normalization
237          */
238         if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
239                 bv.bv_val = in->bv_val + STRLENOF( "dn" );
240
241                 if ( bv.bv_val[ 0 ] == '.' ) {
242                         bv.bv_val++;
243
244                         if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
245                                 bv.bv_val += STRLENOF( "exact:" );
246                                 scope = LDAP_X_SCOPE_EXACT;
247
248                         } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
249                                 bv.bv_val += STRLENOF( "regex:" );
250                                 scope = LDAP_X_SCOPE_REGEX;
251
252                         } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
253                                 bv.bv_val += STRLENOF( "children:" );
254                                 scope = LDAP_X_SCOPE_CHILDREN;
255
256                         } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
257                                 bv.bv_val += STRLENOF( "subtree:" );
258                                 scope = LDAP_X_SCOPE_SUBTREE;
259
260                         } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
261                                 bv.bv_val += STRLENOF( "onelevel:" );
262                                 scope = LDAP_X_SCOPE_ONELEVEL;
263
264                         } else {
265                                 return LDAP_INVALID_SYNTAX;
266                         }
267
268                 } else {
269                         if ( bv.bv_val[ 0 ] != ':' ) {
270                                 return LDAP_INVALID_SYNTAX;
271                         }
272                         scope = LDAP_X_SCOPE_EXACT;
273                         bv.bv_val++;
274                 }
275
276                 bv.bv_val += strspn( bv.bv_val, " " );
277                 /* jump here in case no type specification was present
278                  * and uri was not an URI... HEADS-UP: assuming EXACT */
279 is_dn:          bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
280
281                 /* a single '*' means any DN without using regexes */
282                 if ( ber_bvccmp( &bv, '*' ) ) {
283                         /* LDAP_X_SCOPE_USERS */
284                         return LDAP_SUCCESS;
285                 }
286
287                 switch ( scope ) {
288                 case LDAP_X_SCOPE_EXACT:
289                 case LDAP_X_SCOPE_CHILDREN:
290                 case LDAP_X_SCOPE_SUBTREE:
291                 case LDAP_X_SCOPE_ONELEVEL:
292                         return dnValidate( NULL, &bv );
293
294                 case LDAP_X_SCOPE_REGEX:
295                         return LDAP_SUCCESS;
296                 }
297
298                 return rc;
299
300         /*
301          * 4) u[.mech[/realm]]:<ID>
302          */
303         } else if ( ( in->bv_val[ 0 ] == 'u' || in->bv_val[ 0 ] == 'U' )
304                         && ( in->bv_val[ 1 ] == ':' 
305                                 || in->bv_val[ 1 ] == '/' 
306                                 || in->bv_val[ 1 ] == '.' ) )
307         {
308                 char            buf[ SLAP_LDAPDN_MAXLEN ];
309                 struct berval   id,
310                                 user = BER_BVNULL,
311                                 realm = BER_BVNULL,
312                                 mech = BER_BVNULL;
313
314                 if ( sizeof( buf ) <= in->bv_len ) {
315                         return LDAP_INVALID_SYNTAX;
316                 }
317
318                 id.bv_len = in->bv_len;
319                 id.bv_val = buf;
320                 strncpy( buf, in->bv_val, sizeof( buf ) );
321
322                 rc = slap_parse_user( &id, &user, &realm, &mech );
323                 if ( rc != LDAP_SUCCESS ) {
324                         return LDAP_INVALID_SYNTAX;
325                 }
326
327                 return rc;
328
329         /*
330          * 5) group[/groupClass[/memberAttr]]:<DN>
331          *
332          * <groupClass> defaults to "groupOfNames"
333          * <memberAttr> defaults to "member"
334          * 
335          * <DN> must pass DN normalization
336          */
337         } else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
338         {
339                 struct berval   group_dn = BER_BVNULL,
340                                 group_oc = BER_BVNULL,
341                                 member_at = BER_BVNULL;
342
343                 bv.bv_val = in->bv_val + STRLENOF( "group" );
344                 group_dn.bv_val = strchr( bv.bv_val, ':' );
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
359                         member_at.bv_val = strchr( group_oc.bv_val, '/' );
360                         if ( member_at.bv_val ) {
361                                 AttributeDescription    *ad = NULL;
362                                 const char              *text = NULL;
363
364                                 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
365                                 member_at.bv_val++;
366                                 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
367                                 rc = slap_bv2ad( &member_at, &ad, &text );
368                                 if ( rc != LDAP_SUCCESS ) {
369                                         return rc;
370                                 }
371
372                         } else {
373                                 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
374
375                                 if ( oc_bvfind( &group_oc ) == NULL ) {
376                                         return LDAP_INVALID_SYNTAX;
377                                 }
378                         }
379                 }
380
381                 group_dn.bv_val++;
382                 group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
383
384                 rc = dnValidate( NULL, &group_dn );
385                 if ( rc != LDAP_SUCCESS ) {
386                         return rc;
387                 }
388
389                 return rc;
390         }
391
392         /*
393          * ldap:///<base>??<scope>?<filter>
394          * <scope> ::= {base|one|subtree}
395          *
396          * <scope> defaults to "base"
397          * <base> must pass DN normalization
398          * <filter> must pass str2filter()
399          */
400         rc = ldap_url_parse( in->bv_val, &ludp );
401         switch ( rc ) {
402         case LDAP_URL_SUCCESS:
403                 /* FIXME: the check is pedantic, but I think it's necessary,
404                  * because people tend to use things like ldaps:// which
405                  * gives the idea SSL is being used.  Maybe we could
406                  * accept ldapi:// as well, but the point is that we use
407                  * an URL as an easy means to define bits of a search with
408                  * little parsing.
409                  */
410                 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
411                         /*
412                          * must be ldap:///
413                          */
414                         rc = LDAP_INVALID_SYNTAX;
415                         goto done;
416                 }
417                 break;
418
419         case LDAP_URL_ERR_BADSCHEME:
420                 /*
421                  * last chance: assume it's a(n exact) DN ...
422                  *
423                  * NOTE: must pass DN normalization
424                  */
425                 ldap_free_urldesc( ludp );
426                 bv.bv_val = in->bv_val;
427                 scope = LDAP_X_SCOPE_EXACT;
428                 goto is_dn;
429
430         default:
431                 rc = LDAP_INVALID_SYNTAX;
432                 goto done;
433         }
434
435         if ( ( ludp->lud_host && *ludp->lud_host )
436                 || ludp->lud_attrs || ludp->lud_exts )
437         {
438                 /* host part must be empty */
439                 /* attrs and extensions parts must be empty */
440                 rc = LDAP_INVALID_SYNTAX;
441                 goto done;
442         }
443
444         /* Grab the filter */
445         if ( ludp->lud_filter ) {
446                 Filter  *f = str2filter( ludp->lud_filter );
447                 if ( f == NULL ) {
448                         rc = LDAP_INVALID_SYNTAX;
449                         goto done;
450                 }
451                 filter_free( f );
452         }
453
454         /* Grab the searchbase */
455         assert( ludp->lud_dn != NULL );
456         ber_str2bv( ludp->lud_dn, 0, 0, &bv );
457         rc = dnValidate( NULL, &bv );
458
459 done:
460         ldap_free_urldesc( ludp );
461         return( rc );
462 }
463
464 #if 0
465 int
466 authzMatch(
467         int             *matchp,
468         slap_mask_t     flags,
469         Syntax          *syntax,
470         MatchingRule    *mr,
471         struct berval   *value,
472         void            *assertedValue )
473 {
474         return octetStringMatch( matchp, flags, syntax, mr, value, assertedValue );
475 }
476 #endif
477
478 static int
479 authzPrettyNormal(
480         struct berval   *val,
481         struct berval   *normalized,
482         void            *ctx,
483         int             normalize )
484 {
485         struct berval   bv;
486         int             rc = LDAP_INVALID_SYNTAX;
487         LDAPURLDesc     *ludp = NULL;
488         char            *lud_dn = NULL,
489                         *lud_filter = NULL;
490         int             scope = -1;
491
492         /*
493          * 1) <DN>
494          * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
495          * 3) dn.regex:<pattern>
496          * 4) u[.mech[/realm]]:<ID>
497          * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
498          * 6) <URL>
499          */
500
501         assert( val != NULL );
502         assert( !BER_BVISNULL( val ) );
503
504         /*
505          * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
506          * 3) dn.regex:<pattern>
507          *
508          * <DN> must pass DN normalization
509          */
510         if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
511                 struct berval   out = BER_BVNULL,
512                                 prefix = BER_BVNULL;
513                 char            *ptr;
514
515                 bv.bv_val = val->bv_val + STRLENOF( "dn" );
516
517                 if ( bv.bv_val[ 0 ] == '.' ) {
518                         bv.bv_val++;
519
520                         if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
521                                 bv.bv_val += STRLENOF( "exact:" );
522                                 scope = LDAP_X_SCOPE_EXACT;
523
524                         } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
525                                 bv.bv_val += STRLENOF( "regex:" );
526                                 scope = LDAP_X_SCOPE_REGEX;
527
528                         } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
529                                 bv.bv_val += STRLENOF( "children:" );
530                                 scope = LDAP_X_SCOPE_CHILDREN;
531
532                         } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
533                                 bv.bv_val += STRLENOF( "subtree:" );
534                                 scope = LDAP_X_SCOPE_SUBTREE;
535
536                         } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
537                                 bv.bv_val += STRLENOF( "onelevel:" );
538                                 scope = LDAP_X_SCOPE_ONELEVEL;
539
540                         } else {
541                                 return LDAP_INVALID_SYNTAX;
542                         }
543
544                 } else {
545                         if ( bv.bv_val[ 0 ] != ':' ) {
546                                 return LDAP_INVALID_SYNTAX;
547                         }
548                         scope = LDAP_X_SCOPE_EXACT;
549                         bv.bv_val++;
550                 }
551
552                 bv.bv_val += strspn( bv.bv_val, " " );
553                 /* jump here in case no type specification was present
554                  * and uri was not an URI... HEADS-UP: assuming EXACT */
555 is_dn:          bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
556
557                 /* a single '*' means any DN without using regexes */
558                 if ( ber_bvccmp( &bv, '*' ) ) {
559                         ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
560                         return LDAP_SUCCESS;
561                 }
562
563                 switch ( scope ) {
564                 case LDAP_X_SCOPE_EXACT:
565                 case LDAP_X_SCOPE_CHILDREN:
566                 case LDAP_X_SCOPE_SUBTREE:
567                 case LDAP_X_SCOPE_ONELEVEL:
568                         if ( normalize ) {
569                                 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
570                         } else {
571                                 rc = dnPretty( NULL, &bv, &out, ctx );
572                         }
573                         if( rc != LDAP_SUCCESS ) {
574                                 return LDAP_INVALID_SYNTAX;
575                         }
576                         break;
577
578                 case LDAP_X_SCOPE_REGEX:
579                         normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
580                         normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
581                         ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
582                         ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
583                         ptr[ 0 ] = '\0';
584                         return LDAP_SUCCESS;
585
586                 default:
587                         return LDAP_INVALID_SYNTAX;
588                 }
589
590                 /* prepare prefix */
591                 switch ( scope ) {
592                 case LDAP_X_SCOPE_EXACT:
593                         BER_BVSTR( &prefix, "dn:" );
594                         break;
595
596                 case LDAP_X_SCOPE_CHILDREN:
597                         BER_BVSTR( &prefix, "dn.children:" );
598                         break;
599
600                 case LDAP_X_SCOPE_SUBTREE:
601                         BER_BVSTR( &prefix, "dn.subtree:" );
602                         break;
603
604                 case LDAP_X_SCOPE_ONELEVEL:
605                         BER_BVSTR( &prefix, "dn.onelevel:" );
606                         break;
607
608                 default:
609                         assert( 0 );
610                         break;
611                 }
612
613                 normalized->bv_len = prefix.bv_len + out.bv_len;
614                 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
615                 
616                 ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
617                 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
618                 ptr[ 0 ] = '\0';
619                 ber_memfree_x( out.bv_val, ctx );
620
621                 return LDAP_SUCCESS;
622
623         /*
624          * 4) u[.mech[/realm]]:<ID>
625          */
626         } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
627                         && ( val->bv_val[ 1 ] == ':' 
628                                 || val->bv_val[ 1 ] == '/' 
629                                 || val->bv_val[ 1 ] == '.' ) )
630         {
631                 char            buf[ SLAP_LDAPDN_MAXLEN ];
632                 struct berval   id,
633                                 user = BER_BVNULL,
634                                 realm = BER_BVNULL,
635                                 mech = BER_BVNULL;
636
637                 if ( sizeof( buf ) <= val->bv_len ) {
638                         return LDAP_INVALID_SYNTAX;
639                 }
640
641                 id.bv_len = val->bv_len;
642                 id.bv_val = buf;
643                 strncpy( buf, val->bv_val, sizeof( buf ) );
644
645                 rc = slap_parse_user( &id, &user, &realm, &mech );
646                 if ( rc != LDAP_SUCCESS ) {
647                         return LDAP_INVALID_SYNTAX;
648                 }
649
650                 ber_dupbv_x( normalized, val, ctx );
651
652                 return rc;
653
654         /*
655          * 5) group[/groupClass[/memberAttr]]:<DN>
656          *
657          * <groupClass> defaults to "groupOfNames"
658          * <memberAttr> defaults to "member"
659          * 
660          * <DN> must pass DN normalization
661          */
662         } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
663         {
664                 struct berval   group_dn = BER_BVNULL,
665                                 group_oc = BER_BVNULL,
666                                 member_at = BER_BVNULL,
667                                 out = BER_BVNULL;
668                 char            *ptr;
669
670                 bv.bv_val = val->bv_val + STRLENOF( "group" );
671                 group_dn.bv_val = strchr( bv.bv_val, ':' );
672                 if ( group_dn.bv_val == NULL ) {
673                         /* last chance: assume it's a(n exact) DN ... */
674                         bv.bv_val = val->bv_val;
675                         scope = LDAP_X_SCOPE_EXACT;
676                         goto is_dn;
677                 }
678
679                 /*
680                  * FIXME: we assume that "member" and "groupOfNames"
681                  * are present in schema...
682                  */
683                 if ( bv.bv_val[ 0 ] == '/' ) {
684                         group_oc.bv_val = &bv.bv_val[ 1 ];
685
686                         member_at.bv_val = strchr( group_oc.bv_val, '/' );
687                         if ( member_at.bv_val ) {
688                                 AttributeDescription    *ad = NULL;
689                                 const char              *text = NULL;
690
691                                 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
692                                 member_at.bv_val++;
693                                 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
694                                 rc = slap_bv2ad( &member_at, &ad, &text );
695                                 if ( rc != LDAP_SUCCESS ) {
696                                         return rc;
697                                 }
698
699                                 member_at = ad->ad_cname;
700
701                         } else {
702                                 ObjectClass             *oc = NULL;
703
704                                 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
705
706                                 oc = oc_bvfind( &group_oc );
707                                 if ( oc == NULL ) {
708                                         return LDAP_INVALID_SYNTAX;
709                                 }
710
711                                 group_oc = oc->soc_cname;
712                         }
713                 }
714
715                 group_dn.bv_val++;
716                 group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
717
718                 if ( normalize ) {
719                         rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
720                 } else {
721                         rc = dnPretty( NULL, &group_dn, &out, ctx );
722                 }
723                 if ( rc != LDAP_SUCCESS ) {
724                         return rc;
725                 }
726
727                 normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
728                 if ( !BER_BVISNULL( &group_oc ) ) {
729                         normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
730                         if ( !BER_BVISNULL( &member_at ) ) {
731                                 normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
732                         }
733                 }
734
735                 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
736                 ptr = lutil_strcopy( normalized->bv_val, "group" );
737                 if ( !BER_BVISNULL( &group_oc ) ) {
738                         ptr[ 0 ] = '/';
739                         ptr++;
740                         ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
741                         if ( !BER_BVISNULL( &member_at ) ) {
742                                 ptr[ 0 ] = '/';
743                                 ptr++;
744                                 ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
745                         }
746                 }
747                 ptr[ 0 ] = ':';
748                 ptr++;
749                 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
750                 ptr[ 0 ] = '\0';
751                 ber_memfree_x( out.bv_val, ctx );
752
753                 return rc;
754         }
755
756         /*
757          * ldap:///<base>??<scope>?<filter>
758          * <scope> ::= {base|one|subtree}
759          *
760          * <scope> defaults to "base"
761          * <base> must pass DN normalization
762          * <filter> must pass str2filter()
763          */
764         rc = ldap_url_parse( val->bv_val, &ludp );
765         switch ( rc ) {
766         case LDAP_URL_SUCCESS:
767                 /* FIXME: the check is pedantic, but I think it's necessary,
768                  * because people tend to use things like ldaps:// which
769                  * gives the idea SSL is being used.  Maybe we could
770                  * accept ldapi:// as well, but the point is that we use
771                  * an URL as an easy means to define bits of a search with
772                  * little parsing.
773                  */
774                 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
775                         /*
776                          * must be ldap:///
777                          */
778                         rc = LDAP_INVALID_SYNTAX;
779                         goto done;
780                 }
781
782                 AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
783                 break;
784
785         case LDAP_URL_ERR_BADSCHEME:
786                 /*
787                  * last chance: assume it's a(n exact) DN ...
788                  *
789                  * NOTE: must pass DN normalization
790                  */
791                 ldap_free_urldesc( ludp );
792                 bv.bv_val = val->bv_val;
793                 scope = LDAP_X_SCOPE_EXACT;
794                 goto is_dn;
795
796         default:
797                 rc = LDAP_INVALID_SYNTAX;
798                 goto done;
799         }
800
801         if ( ( ludp->lud_host && *ludp->lud_host )
802                 || ludp->lud_attrs || ludp->lud_exts )
803         {
804                 /* host part must be empty */
805                 /* attrs and extensions parts must be empty */
806                 rc = LDAP_INVALID_SYNTAX;
807                 goto done;
808         }
809
810         /* Grab the filter */
811         if ( ludp->lud_filter ) {
812                 struct berval   filterstr;
813                 Filter          *f;
814
815                 lud_filter = ludp->lud_filter;
816
817                 f = str2filter( lud_filter );
818                 if ( f == NULL ) {
819                         rc = LDAP_INVALID_SYNTAX;
820                         goto done;
821                 }
822                 filter2bv( f, &filterstr );
823                 filter_free( f );
824                 if ( BER_BVISNULL( &filterstr ) ) {
825                         rc = LDAP_INVALID_SYNTAX;
826                         goto done;
827                 }
828
829                 ludp->lud_filter = filterstr.bv_val;
830         }
831
832         /* Grab the searchbase */
833         assert( ludp->lud_dn != NULL );
834         if ( ludp->lud_dn ) {
835                 struct berval   out = BER_BVNULL;
836
837                 lud_dn = ludp->lud_dn;
838
839                 ber_str2bv( lud_dn, 0, 0, &bv );
840                 if ( normalize ) {
841                         rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
842                 } else {
843                         rc = dnPretty( NULL, &bv, &out, ctx );
844                 }
845
846                 if ( rc != LDAP_SUCCESS ) {
847                         goto done;
848                 }
849
850                 ludp->lud_dn = out.bv_val;
851         }
852
853         ludp->lud_port = 0;
854         normalized->bv_val = ldap_url_desc2str( ludp );
855         if ( normalized->bv_val ) {
856                 normalized->bv_len = strlen( normalized->bv_val );
857
858         } else {
859                 rc = LDAP_INVALID_SYNTAX;
860         }
861
862 done:
863         if ( lud_filter ) {
864                 if ( ludp->lud_filter != lud_filter ) {
865                         ber_memfree( ludp->lud_filter );
866                 }
867                 ludp->lud_filter = lud_filter;
868         }
869
870         if ( lud_dn ) {
871                 if ( ludp->lud_dn != lud_dn ) {
872                         ber_memfree( ludp->lud_dn );
873                 }
874                 ludp->lud_dn = lud_dn;
875         }
876
877         ldap_free_urldesc( ludp );
878
879         return( rc );
880 }
881
882 int
883 authzNormalize(
884         slap_mask_t     usage,
885         Syntax          *syntax,
886         MatchingRule    *mr,
887         struct berval   *val,
888         struct berval   *normalized,
889         void            *ctx )
890 {
891         int             rc;
892
893         Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
894                 val->bv_val, 0, 0 );
895
896         rc = authzPrettyNormal( val, normalized, ctx, 1 );
897
898         Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
899                 normalized->bv_val, rc, 0 );
900
901         return rc;
902 }
903
904 int
905 authzPretty(
906         Syntax *syntax,
907         struct berval *val,
908         struct berval *out,
909         void *ctx)
910 {
911         int             rc;
912
913         Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
914                 val->bv_val, 0, 0 );
915
916         rc = authzPrettyNormal( val, out, ctx, 0 );
917
918         Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
919                 out->bv_val, rc, 0 );
920
921         return rc;
922 }
923
924 #endif /* SLAP_AUTHZ_SYNTAX */
925
926 static int
927 slap_parseURI(
928         Operation       *op,
929         struct berval   *uri,
930         struct berval   *base,
931         struct berval   *nbase,
932         int             *scope,
933         Filter          **filter,
934         struct berval   *fstr,
935         int             normalize )
936 {
937         struct berval   bv;
938         int             rc;
939         LDAPURLDesc     *ludp;
940
941 #ifdef SLAP_ORDERED_PRETTYNORM
942         struct berval   idx;
943 #endif /* SLAP_ORDERED_PRETTYNORM */
944
945         assert( uri != NULL && !BER_BVISNULL( uri ) );
946         BER_BVZERO( base );
947         BER_BVZERO( nbase );
948         BER_BVZERO( fstr );
949         *scope = -1;
950         *filter = NULL;
951
952         Debug( LDAP_DEBUG_TRACE,
953                 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
954
955         rc = LDAP_PROTOCOL_ERROR;
956
957 #ifdef SLAP_ORDERED_PRETTYNORM
958         idx = *uri;
959         if ( idx.bv_val[ 0 ] == '{' ) {
960                 char    *ptr;
961
962                 ptr = strchr( idx.bv_val, '}' ) + 1;
963
964                 assert( ptr != (void *)1 );
965
966                 idx.bv_len -= ptr - idx.bv_val;
967                 idx.bv_val = ptr;
968                 uri = &idx;
969         }
970 #endif /* SLAP_ORDERED_PRETTYNORM */
971
972         /*
973          * dn[.<dnstyle>]:<dnpattern>
974          * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
975          *
976          * <dnstyle> defaults to "exact"
977          * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
978          */
979         if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
980                 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
981
982                 if ( bv.bv_val[ 0 ] == '.' ) {
983                         bv.bv_val++;
984
985                         if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
986                                 bv.bv_val += STRLENOF( "exact:" );
987                                 *scope = LDAP_X_SCOPE_EXACT;
988
989                         } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
990                                 bv.bv_val += STRLENOF( "regex:" );
991                                 *scope = LDAP_X_SCOPE_REGEX;
992
993                         } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
994                                 bv.bv_val += STRLENOF( "children:" );
995                                 *scope = LDAP_X_SCOPE_CHILDREN;
996
997                         } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
998                                 bv.bv_val += STRLENOF( "subtree:" );
999                                 *scope = LDAP_X_SCOPE_SUBTREE;
1000
1001                         } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
1002                                 bv.bv_val += STRLENOF( "onelevel:" );
1003                                 *scope = LDAP_X_SCOPE_ONELEVEL;
1004
1005                         } else {
1006                                 return LDAP_PROTOCOL_ERROR;
1007                         }
1008
1009                 } else {
1010                         if ( bv.bv_val[ 0 ] != ':' ) {
1011                                 return LDAP_PROTOCOL_ERROR;
1012                         }
1013                         *scope = LDAP_X_SCOPE_EXACT;
1014                         bv.bv_val++;
1015                 }
1016
1017                 bv.bv_val += strspn( bv.bv_val, " " );
1018                 /* jump here in case no type specification was present
1019                  * and uri was not an URI... HEADS-UP: assuming EXACT */
1020 is_dn:          bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
1021
1022                 /* a single '*' means any DN without using regexes */
1023                 if ( ber_bvccmp( &bv, '*' ) ) {
1024                         *scope = LDAP_X_SCOPE_USERS;
1025                 }
1026
1027                 switch ( *scope ) {
1028                 case LDAP_X_SCOPE_EXACT:
1029                 case LDAP_X_SCOPE_CHILDREN:
1030                 case LDAP_X_SCOPE_SUBTREE:
1031                 case LDAP_X_SCOPE_ONELEVEL:
1032                         if ( normalize ) {
1033                                 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
1034                                 if( rc != LDAP_SUCCESS ) {
1035                                         *scope = -1;
1036                                 }
1037                         } else {
1038                                 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1039                                 rc = LDAP_SUCCESS;
1040                         }
1041                         break;
1042
1043                 case LDAP_X_SCOPE_REGEX:
1044                         ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1045
1046                 case LDAP_X_SCOPE_USERS:
1047                         rc = LDAP_SUCCESS;
1048                         break;
1049
1050                 default:
1051                         *scope = -1;
1052                         break;
1053                 }
1054
1055                 return rc;
1056
1057         /*
1058          * u:<uid>
1059          */
1060         } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
1061                         && ( uri->bv_val[ 1 ] == ':' 
1062                                 || uri->bv_val[ 1 ] == '/' 
1063                                 || uri->bv_val[ 1 ] == '.' ) )
1064         {
1065                 Connection      c = *op->o_conn;
1066                 char            buf[ SLAP_LDAPDN_MAXLEN ];
1067                 struct berval   id,
1068                                 user = BER_BVNULL,
1069                                 realm = BER_BVNULL,
1070                                 mech = BER_BVNULL;
1071
1072                 if ( sizeof( buf ) <= uri->bv_len ) {
1073                         return LDAP_INVALID_SYNTAX;
1074                 }
1075
1076                 id.bv_len = uri->bv_len;
1077                 id.bv_val = buf;
1078                 strncpy( buf, uri->bv_val, sizeof( buf ) );
1079
1080                 rc = slap_parse_user( &id, &user, &realm, &mech );
1081                 if ( rc != LDAP_SUCCESS ) {
1082                         return rc;
1083                 }
1084
1085                 if ( !BER_BVISNULL( &mech ) ) {
1086                         c.c_sasl_bind_mech = mech;
1087                 } else {
1088                         BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1089                 }
1090                 
1091                 rc = slap_sasl_getdn( &c, op, &user,
1092                                 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1093
1094                 if ( rc == LDAP_SUCCESS ) {
1095                         *scope = LDAP_X_SCOPE_EXACT;
1096                 }
1097
1098                 return rc;
1099
1100         /*
1101          * group[/<groupoc>[/<groupat>]]:<groupdn>
1102          *
1103          * groupoc defaults to "groupOfNames"
1104          * groupat defaults to "member"
1105          * 
1106          * <groupdn> must pass DN normalization
1107          */
1108         } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1109         {
1110                 struct berval   group_dn = BER_BVNULL,
1111                                 group_oc = BER_BVNULL,
1112                                 member_at = BER_BVNULL;
1113                 char            *tmp;
1114
1115                 bv.bv_val = uri->bv_val + STRLENOF( "group" );
1116                 group_dn.bv_val = strchr( bv.bv_val, ':' );
1117                 if ( group_dn.bv_val == NULL ) {
1118                         /* last chance: assume it's a(n exact) DN ... */
1119                         bv.bv_val = uri->bv_val;
1120                         *scope = LDAP_X_SCOPE_EXACT;
1121                         goto is_dn;
1122                 }
1123                 
1124                 if ( bv.bv_val[ 0 ] == '/' ) {
1125                         group_oc.bv_val = &bv.bv_val[ 1 ];
1126
1127                         member_at.bv_val = strchr( group_oc.bv_val, '/' );
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                                 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
1135                                 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1136                         }
1137
1138                 } else {
1139                         BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
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                 return( LDAP_OTHER );
1401         }
1402
1403         rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
1404         if ( rc != LDAP_SUCCESS ) return rc;
1405
1406         nSaslRegexp++;
1407         return( LDAP_SUCCESS );
1408 #endif /* ! SLAP_AUTH_REWRITE */
1409 }
1410
1411 void slap_sasl_regexp_unparse( BerVarray *out )
1412 {
1413         int i;
1414         BerVarray bva = NULL;
1415         char ibuf[32], *ptr;
1416         struct berval idx;
1417
1418         if ( !nSaslRegexp ) return;
1419
1420         idx.bv_val = ibuf;
1421         bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1422         BER_BVZERO(bva+nSaslRegexp);
1423         for ( i=0; i<nSaslRegexp; i++ ) {
1424                 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1425                 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1426                         strlen( SaslRegexp[i].sr_replace ) + 5;
1427                 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1428                 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1429                 *ptr++ = '"';
1430                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1431                 ptr = lutil_strcopy( ptr, "\" \"" );
1432                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1433                 *ptr++ = '"';
1434                 *ptr = '\0';
1435         }
1436         *out = bva;
1437 }
1438
1439 #ifndef SLAP_AUTH_REWRITE
1440 /* Perform replacement on regexp matches */
1441 static void slap_sasl_rx_exp(
1442         const char *rep,
1443         const int *off,
1444         regmatch_t *str,
1445         const char *saslname,
1446         struct berval *out,
1447         void *ctx )
1448 {
1449         int i, n, len, insert;
1450
1451         /* Get the total length of the final URI */
1452
1453         n=1;
1454         len = 0;
1455         while( off[n] >= 0 ) {
1456                 /* Len of next section from replacement string (x,y,z above) */
1457                 len += off[n] - off[n-1] - 2;
1458                 if( off[n+1] < 0)
1459                         break;
1460
1461                 /* Len of string from saslname that matched next $i  (b,d above) */
1462                 i = rep[ off[n] + 1 ]   - '0';
1463                 len += str[i].rm_eo - str[i].rm_so;
1464                 n++;
1465         }
1466         out->bv_val = slap_sl_malloc( len + 1, ctx );
1467         out->bv_len = len;
1468
1469         /* Fill in URI with replace string, replacing $i as we go */
1470         n=1;
1471         insert = 0;
1472         while( off[n] >= 0) {
1473                 /* Paste in next section from replacement string (x,y,z above) */
1474                 len = off[n] - off[n-1] - 2;
1475                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1476                 insert += len;
1477                 if( off[n+1] < 0)
1478                         break;
1479
1480                 /* Paste in string from saslname that matched next $i  (b,d above) */
1481                 i = rep[ off[n] + 1 ]   - '0';
1482                 len = str[i].rm_eo - str[i].rm_so;
1483                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1484                 insert += len;
1485
1486                 n++;
1487         }
1488
1489         out->bv_val[insert] = '\0';
1490 }
1491 #endif /* ! SLAP_AUTH_REWRITE */
1492
1493 /* Take the passed in SASL name and attempt to convert it into an
1494    LDAP URI to find the matching LDAP entry, using the pattern matching
1495    strings given in the saslregexp config file directive(s) */
1496
1497 static int slap_authz_regexp( struct berval *in, struct berval *out,
1498                 int flags, void *ctx )
1499 {
1500 #ifdef SLAP_AUTH_REWRITE
1501         const char      *context = AUTHID_CONTEXT;
1502
1503         if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1504                 return 0;
1505         }
1506
1507         /* FIXME: if aware of authc/authz mapping, 
1508          * we could use different contexts ... */
1509         switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 
1510                                 &out->bv_val ) )
1511         {
1512         case REWRITE_REGEXEC_OK:
1513                 if ( !BER_BVISNULL( out ) ) {
1514                         char *val = out->bv_val;
1515                         ber_str2bv_x( val, 0, 1, out, ctx );
1516                         if ( val != in->bv_val ) {
1517                                 free( val );
1518                         }
1519                 } else {
1520                         ber_dupbv_x( out, in, ctx );
1521                 }
1522                 Debug( LDAP_DEBUG_ARGS,
1523                         "[rw] %s: \"%s\" -> \"%s\"\n",
1524                         context, in->bv_val, out->bv_val );             
1525                 return 1;
1526                 
1527         case REWRITE_REGEXEC_UNWILLING:
1528         case REWRITE_REGEXEC_ERR:
1529         default:
1530                 return 0;
1531         }
1532
1533 #else /* ! SLAP_AUTH_REWRITE */
1534         char *saslname = in->bv_val;
1535         SaslRegexp_t *reg;
1536         regmatch_t sr_strings[SASLREGEX_REPLACE];       /* strings matching $1,$2 ... */
1537         int i;
1538
1539         memset( out, 0, sizeof( *out ) );
1540
1541         Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1542            saslname, 0, 0 );
1543
1544         if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1545                 return( 0 );
1546         }
1547
1548         /* Match the normalized SASL name to the saslregexp patterns */
1549         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
1550                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
1551                   sr_strings, 0)  == 0 )
1552                         break;
1553         }
1554
1555         if( i >= nSaslRegexp ) return( 0 );
1556
1557         /*
1558          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1559          * replace pattern of the form "x$1y$2z". The returned string needs
1560          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1561          */
1562         slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1563                 sr_strings, saslname, out, ctx );
1564
1565         Debug( LDAP_DEBUG_TRACE,
1566                 "slap_authz_regexp: converted SASL name to %s\n",
1567                 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1568
1569         return( 1 );
1570 #endif /* ! SLAP_AUTH_REWRITE */
1571 }
1572
1573 /* This callback actually does some work...*/
1574 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
1575 {
1576         struct berval *ndn = o->o_callback->sc_private;
1577
1578         if (rs->sr_type != REP_SEARCH) return 0;
1579
1580         /* We only want to be called once */
1581         if ( !BER_BVISNULL( ndn ) ) {
1582                 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
1583                 BER_BVZERO( ndn );
1584
1585                 Debug( LDAP_DEBUG_TRACE,
1586                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
1587                 return -1;
1588         }
1589
1590         ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
1591         return 0;
1592 }
1593
1594
1595 typedef struct smatch_info {
1596         struct berval *dn;
1597         int match;
1598 } smatch_info;
1599
1600 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1601 {
1602         smatch_info *sm = o->o_callback->sc_private;
1603
1604         if ( rs->sr_type != REP_SEARCH ) {
1605                 if ( rs->sr_err != LDAP_SUCCESS ) {
1606                         sm->match = -1;
1607                 }
1608                 return 0;
1609         }
1610
1611         if ( sm->match == 1 ) {
1612                 sm->match = -1;
1613                 return 0;
1614         }
1615
1616         if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1617                 sm->match = 1;
1618
1619         } else {
1620                 sm->match = -1;
1621         }
1622
1623         return 0;
1624 }
1625
1626 int
1627 slap_sasl_matches( Operation *op, BerVarray rules,
1628                 struct berval *assertDN, struct berval *authc )
1629 {
1630         int     rc = LDAP_INAPPROPRIATE_AUTH;
1631
1632         if ( rules != NULL ) {
1633                 int     i;
1634
1635                 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1636                         rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1637                         if ( rc == LDAP_SUCCESS ) break;
1638                 }
1639         }
1640         
1641         return rc;
1642 }
1643
1644 /*
1645  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1646  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1647  * the rule must be used as an internal search for entries. If that search
1648  * returns the *assertDN entry, the match is successful.
1649  *
1650  * The assertDN should not have the dn: prefix
1651  */
1652
1653 static int
1654 slap_sasl_match( Operation *opx, struct berval *rule,
1655         struct berval *assertDN, struct berval *authc )
1656 {
1657         int rc; 
1658         regex_t reg;
1659         smatch_info sm;
1660         slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1661         Operation op = {0};
1662         SlapReply rs = {REP_RESULT};
1663         struct berval base = BER_BVNULL;
1664
1665         sm.dn = assertDN;
1666         sm.match = 0;
1667         cb.sc_private = &sm;
1668
1669         Debug( LDAP_DEBUG_TRACE,
1670            "===>slap_sasl_match: comparing DN %s to rule %s\n",
1671                 assertDN->bv_val, rule->bv_val, 0 );
1672
1673         /* NOTE: don't normalize rule if authz syntax is enabled */
1674         rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1675                 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 
1676 #ifdef SLAP_AUTHZ_SYNTAX
1677                 0
1678 #else /* ! SLAP_AUTHZ_SYNTAX */
1679                 1
1680 #endif /* ! SLAP_AUTHZ_SYNTAX */
1681                 );
1682
1683         if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1684
1685         switch ( op.ors_scope ) {
1686         case LDAP_X_SCOPE_EXACT:
1687 exact_match:
1688                 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1689                         rc = LDAP_SUCCESS;
1690                 } else {
1691                         rc = LDAP_INAPPROPRIATE_AUTH;
1692                 }
1693                 goto CONCLUDED;
1694
1695         case LDAP_X_SCOPE_CHILDREN:
1696         case LDAP_X_SCOPE_SUBTREE:
1697         case LDAP_X_SCOPE_ONELEVEL:
1698         {
1699                 int     d = assertDN->bv_len - op.o_req_ndn.bv_len;
1700
1701                 rc = LDAP_INAPPROPRIATE_AUTH;
1702
1703                 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1704                         goto exact_match;
1705
1706                 } else if ( d > 0 ) {
1707                         struct berval bv;
1708
1709                         /* leave room for at least one char of attributeType,
1710                          * one for '=' and one for ',' */
1711                         if ( d < STRLENOF( "x=,") ) {
1712                                 goto CONCLUDED;
1713                         }
1714
1715                         bv.bv_len = op.o_req_ndn.bv_len;
1716                         bv.bv_val = assertDN->bv_val + d;
1717
1718                         if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1719                                 switch ( op.ors_scope ) {
1720                                 case LDAP_X_SCOPE_SUBTREE:
1721                                 case LDAP_X_SCOPE_CHILDREN:
1722                                         rc = LDAP_SUCCESS;
1723                                         break;
1724
1725                                 case LDAP_X_SCOPE_ONELEVEL:
1726                                 {
1727                                         struct berval   pdn;
1728
1729                                         dnParent( assertDN, &pdn );
1730                                         /* the common portion of the DN
1731                                          * already matches, so only check
1732                                          * if parent DN of assertedDN 
1733                                          * is all the pattern */
1734                                         if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1735                                                 rc = LDAP_SUCCESS;
1736                                         }
1737                                         break;
1738                                 }
1739                                 default:
1740                                         /* at present, impossible */
1741                                         assert( 0 );
1742                                 }
1743                         }
1744                 }
1745                 goto CONCLUDED;
1746         }
1747
1748         case LDAP_X_SCOPE_REGEX:
1749                 rc = regcomp(&reg, op.o_req_ndn.bv_val,
1750                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
1751                 if ( rc == 0 ) {
1752                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
1753                         regfree( &reg );
1754                 }
1755                 if ( rc == 0 ) {
1756                         rc = LDAP_SUCCESS;
1757                 } else {
1758                         rc = LDAP_INAPPROPRIATE_AUTH;
1759                 }
1760                 goto CONCLUDED;
1761
1762         case LDAP_X_SCOPE_GROUP: {
1763                 char    *tmp;
1764
1765                 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1766                  * we need to append the <assertDN> so that the <group_dn> is searched
1767                  * with scope "base", and the filter ensures that <assertDN> is
1768                  * member of the group */
1769                 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1770                         assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1771                 if ( tmp == NULL ) {
1772                         rc = LDAP_NO_MEMORY;
1773                         goto CONCLUDED;
1774                 }
1775                 op.ors_filterstr.bv_val = tmp;
1776                 
1777                 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1778                 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1779
1780                 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1781                 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1782                 if ( op.ors_filter == NULL ) {
1783                         rc = LDAP_PROTOCOL_ERROR;
1784                         goto CONCLUDED;
1785                 }
1786                 op.ors_scope = LDAP_SCOPE_BASE;
1787
1788                 /* hijack match DN: use that of the group instead of the assertDN;
1789                  * assertDN is now in the filter */
1790                 sm.dn = &op.o_req_ndn;
1791
1792                 /* do the search */
1793                 break;
1794                 }
1795
1796         case LDAP_X_SCOPE_USERS:
1797                 if ( !BER_BVISEMPTY( assertDN ) ) {
1798                         rc = LDAP_SUCCESS;
1799                 } else {
1800                         rc = LDAP_INAPPROPRIATE_AUTH;
1801                 }
1802                 goto CONCLUDED;
1803
1804         default:
1805                 break;
1806         }
1807
1808         /* Must run an internal search. */
1809         if ( op.ors_filter == NULL ) {
1810                 rc = LDAP_FILTER_ERROR;
1811                 goto CONCLUDED;
1812         }
1813
1814         Debug( LDAP_DEBUG_TRACE,
1815            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1816            op.o_req_ndn.bv_val, op.ors_scope, 0 );
1817
1818         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1819         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1820                 rc = LDAP_INAPPROPRIATE_AUTH;
1821                 goto CONCLUDED;
1822         }
1823
1824         op.o_hdr = opx->o_hdr;
1825         op.o_tag = LDAP_REQ_SEARCH;
1826         op.o_ndn = *authc;
1827         op.o_callback = &cb;
1828         op.o_time = slap_get_time();
1829         op.o_do_not_cache = 1;
1830         op.o_is_auth_check = 1;
1831         /* use req_ndn as req_dn instead of non-pretty base of uri */
1832         if( !BER_BVISNULL( &base ) ) {
1833                 ch_free( base.bv_val );
1834                 /* just in case... */
1835                 BER_BVZERO( &base );
1836         }
1837         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1838         op.ors_deref = LDAP_DEREF_NEVER;
1839         op.ors_slimit = 1;
1840         op.ors_tlimit = SLAP_NO_LIMIT;
1841         op.ors_attrs = slap_anlist_no_attrs;
1842         op.ors_attrsonly = 1;
1843
1844         op.o_bd->be_search( &op, &rs );
1845
1846         if (sm.match == 1) {
1847                 rc = LDAP_SUCCESS;
1848         } else {
1849                 rc = LDAP_INAPPROPRIATE_AUTH;
1850         }
1851
1852 CONCLUDED:
1853         if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1854         if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1855         if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1856         if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1857
1858         Debug( LDAP_DEBUG_TRACE,
1859            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1860
1861         return( rc );
1862 }
1863
1864
1865 /*
1866  * This function answers the question, "Can this ID authorize to that ID?",
1867  * based on authorization rules. The rules are stored in the *searchDN, in the
1868  * attribute named by *attr. If any of those rules map to the *assertDN, the
1869  * authorization is approved.
1870  *
1871  * The DNs should not have the dn: prefix
1872  */
1873 static int
1874 slap_sasl_check_authz( Operation *op,
1875         struct berval *searchDN,
1876         struct berval *assertDN,
1877         AttributeDescription *ad,
1878         struct berval *authc )
1879 {
1880         int rc;
1881         BerVarray vals = NULL;
1882
1883         Debug( LDAP_DEBUG_TRACE,
1884            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1885            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1886
1887         rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1888         if( rc != LDAP_SUCCESS ) goto COMPLETE;
1889
1890         /* Check if the *assertDN matches any *vals */
1891         rc = slap_sasl_matches( op, vals, assertDN, authc );
1892
1893 COMPLETE:
1894         if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1895
1896         Debug( LDAP_DEBUG_TRACE,
1897            "<==slap_sasl_check_authz: %s check returning %d\n",
1898                 ad->ad_cname.bv_val, rc, 0);
1899
1900         return( rc );
1901 }
1902
1903 /*
1904  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1905  * return the LDAP DN to which it matches. The SASL regexp rules in the config
1906  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1907  * search with scope=base), just return the URI (or its searchbase). Otherwise
1908  * an internal search must be done, and if that search returns exactly one
1909  * entry, return the DN of that one entry.
1910  */
1911 void
1912 slap_sasl2dn(
1913         Operation       *opx,
1914         struct berval   *saslname,
1915         struct berval   *sasldn,
1916         int             flags )
1917 {
1918         int rc;
1919         slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1920         Operation op = {0};
1921         SlapReply rs = {REP_RESULT};
1922         struct berval regout = BER_BVNULL;
1923         struct berval base = BER_BVNULL;
1924
1925         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1926                 "converting SASL name %s to a DN\n",
1927                 saslname->bv_val, 0,0 );
1928
1929         BER_BVZERO( sasldn );
1930         cb.sc_private = sasldn;
1931
1932         /* Convert the SASL name into a minimal URI */
1933         if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
1934                 goto FINISHED;
1935         }
1936
1937         /* NOTE: always normalize regout because it results
1938          * from string submatch expansion */
1939         rc = slap_parseURI( opx, &regout, &base, &op.o_req_ndn,
1940                 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1941         if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1942         if ( rc != LDAP_SUCCESS ) {
1943                 goto FINISHED;
1944         }
1945
1946         /* Must do an internal search */
1947         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1948
1949         switch ( op.ors_scope ) {
1950         case LDAP_X_SCOPE_EXACT:
1951                 *sasldn = op.o_req_ndn;
1952                 BER_BVZERO( &op.o_req_ndn );
1953                 /* intentionally continue to next case */
1954
1955         case LDAP_X_SCOPE_REGEX:
1956         case LDAP_X_SCOPE_SUBTREE:
1957         case LDAP_X_SCOPE_CHILDREN:
1958         case LDAP_X_SCOPE_ONELEVEL:
1959         case LDAP_X_SCOPE_GROUP:
1960         case LDAP_X_SCOPE_USERS:
1961                 /* correctly parsed, but illegal */
1962                 goto FINISHED;
1963
1964         case LDAP_SCOPE_BASE:
1965         case LDAP_SCOPE_ONELEVEL:
1966         case LDAP_SCOPE_SUBTREE:
1967 #ifdef LDAP_SCOPE_SUBORDINATE
1968         case LDAP_SCOPE_SUBORDINATE:
1969 #endif
1970                 /* do a search */
1971                 break;
1972
1973         default:
1974                 /* catch unhandled cases (there shouldn't be) */
1975                 assert( 0 );
1976         }
1977
1978         Debug( LDAP_DEBUG_TRACE,
1979                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1980                 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1981
1982         if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1983                 goto FINISHED;
1984         }
1985
1986         /* Must run an internal search. */
1987         if ( op.ors_filter == NULL ) {
1988                 rc = LDAP_FILTER_ERROR;
1989                 goto FINISHED;
1990         }
1991
1992         op.o_hdr = opx->o_hdr;
1993         op.o_tag = LDAP_REQ_SEARCH;
1994         op.o_ndn = opx->o_conn->c_ndn;
1995         op.o_callback = &cb;
1996         op.o_time = slap_get_time();
1997         op.o_do_not_cache = 1;
1998         op.o_is_auth_check = 1;
1999         op.ors_deref = LDAP_DEREF_NEVER;
2000         op.ors_slimit = 1;
2001         op.ors_tlimit = SLAP_NO_LIMIT;
2002         op.ors_attrs = slap_anlist_no_attrs;
2003         op.ors_attrsonly = 1;
2004         /* use req_ndn as req_dn instead of non-pretty base of uri */
2005         if( !BER_BVISNULL( &base ) ) {
2006                 ch_free( base.bv_val );
2007                 /* just in case... */
2008                 BER_BVZERO( &base );
2009         }
2010         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2011
2012         op.o_bd->be_search( &op, &rs );
2013         
2014 FINISHED:
2015         if( !BER_BVISEMPTY( sasldn ) ) {
2016                 opx->o_conn->c_authz_backend = op.o_bd;
2017         }
2018         if( !BER_BVISNULL( &op.o_req_dn ) ) {
2019                 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2020         }
2021         if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2022                 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2023         }
2024         if( op.ors_filter ) {
2025                 filter_free_x( opx, op.ors_filter );
2026         }
2027         if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2028                 ch_free( op.ors_filterstr.bv_val );
2029         }
2030
2031         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2032                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2033
2034         return;
2035 }
2036
2037
2038 /* Check if a bind can SASL authorize to another identity.
2039  * The DNs should not have the dn: prefix
2040  */
2041
2042 int slap_sasl_authorized( Operation *op,
2043         struct berval *authcDN, struct berval *authzDN )
2044 {
2045         int rc = LDAP_INAPPROPRIATE_AUTH;
2046
2047         /* User binding as anonymous */
2048         if ( authzDN == NULL ) {
2049                 rc = LDAP_SUCCESS;
2050                 goto DONE;
2051         }
2052
2053         Debug( LDAP_DEBUG_TRACE,
2054            "==>slap_sasl_authorized: can %s become %s?\n",
2055                 authcDN->bv_val, authzDN->bv_val, 0 );
2056
2057         /* If person is authorizing to self, succeed */
2058         if ( dn_match( authcDN, authzDN ) ) {
2059                 rc = LDAP_SUCCESS;
2060                 goto DONE;
2061         }
2062
2063         /* Allow the manager to authorize as any DN. */
2064         if( op->o_conn->c_authz_backend &&
2065                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2066         {
2067                 rc = LDAP_SUCCESS;
2068                 goto DONE;
2069         }
2070
2071         /* Check source rules */
2072         if( authz_policy & SASL_AUTHZ_TO ) {
2073                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2074                         slap_schema.si_ad_saslAuthzTo, authcDN );
2075                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2076                         goto DONE;
2077                 }
2078         }
2079
2080         /* Check destination rules */
2081         if( authz_policy & SASL_AUTHZ_FROM ) {
2082                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2083                         slap_schema.si_ad_saslAuthzFrom, authcDN );
2084                 if( rc == LDAP_SUCCESS ) {
2085                         goto DONE;
2086                 }
2087         }
2088
2089         rc = LDAP_INAPPROPRIATE_AUTH;
2090
2091 DONE:
2092
2093         Debug( LDAP_DEBUG_TRACE,
2094                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
2095
2096         return( rc );
2097 }