]> 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 #ifdef ENABLE_REWRITE
1401         /* Dummy block to force symbol references in librewrite */
1402         if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1403                 rewrite_info_init( 0 );
1404         }
1405 #endif
1406                 return( LDAP_OTHER );
1407         }
1408
1409         rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
1410         if ( rc != LDAP_SUCCESS ) return rc;
1411
1412         nSaslRegexp++;
1413         return( LDAP_SUCCESS );
1414 #endif /* ! SLAP_AUTH_REWRITE */
1415 }
1416
1417 void slap_sasl_regexp_unparse( BerVarray *out )
1418 {
1419         int i;
1420         BerVarray bva = NULL;
1421         char ibuf[32], *ptr;
1422         struct berval idx;
1423
1424         if ( !nSaslRegexp ) return;
1425
1426         idx.bv_val = ibuf;
1427         bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1428         BER_BVZERO(bva+nSaslRegexp);
1429         for ( i=0; i<nSaslRegexp; i++ ) {
1430                 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1431                 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1432                         strlen( SaslRegexp[i].sr_replace ) + 5;
1433                 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1434                 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1435                 *ptr++ = '"';
1436                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1437                 ptr = lutil_strcopy( ptr, "\" \"" );
1438                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1439                 *ptr++ = '"';
1440                 *ptr = '\0';
1441         }
1442         *out = bva;
1443 }
1444
1445 #ifndef SLAP_AUTH_REWRITE
1446 /* Perform replacement on regexp matches */
1447 static void slap_sasl_rx_exp(
1448         const char *rep,
1449         const int *off,
1450         regmatch_t *str,
1451         const char *saslname,
1452         struct berval *out,
1453         void *ctx )
1454 {
1455         int i, n, len, insert;
1456
1457         /* Get the total length of the final URI */
1458
1459         n=1;
1460         len = 0;
1461         while( off[n] >= 0 ) {
1462                 /* Len of next section from replacement string (x,y,z above) */
1463                 len += off[n] - off[n-1] - 2;
1464                 if( off[n+1] < 0)
1465                         break;
1466
1467                 /* Len of string from saslname that matched next $i  (b,d above) */
1468                 i = rep[ off[n] + 1 ]   - '0';
1469                 len += str[i].rm_eo - str[i].rm_so;
1470                 n++;
1471         }
1472         out->bv_val = slap_sl_malloc( len + 1, ctx );
1473         out->bv_len = len;
1474
1475         /* Fill in URI with replace string, replacing $i as we go */
1476         n=1;
1477         insert = 0;
1478         while( off[n] >= 0) {
1479                 /* Paste in next section from replacement string (x,y,z above) */
1480                 len = off[n] - off[n-1] - 2;
1481                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1482                 insert += len;
1483                 if( off[n+1] < 0)
1484                         break;
1485
1486                 /* Paste in string from saslname that matched next $i  (b,d above) */
1487                 i = rep[ off[n] + 1 ]   - '0';
1488                 len = str[i].rm_eo - str[i].rm_so;
1489                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1490                 insert += len;
1491
1492                 n++;
1493         }
1494
1495         out->bv_val[insert] = '\0';
1496 }
1497 #endif /* ! SLAP_AUTH_REWRITE */
1498
1499 /* Take the passed in SASL name and attempt to convert it into an
1500    LDAP URI to find the matching LDAP entry, using the pattern matching
1501    strings given in the saslregexp config file directive(s) */
1502
1503 static int slap_authz_regexp( struct berval *in, struct berval *out,
1504                 int flags, void *ctx )
1505 {
1506 #ifdef SLAP_AUTH_REWRITE
1507         const char      *context = AUTHID_CONTEXT;
1508
1509         if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1510                 return 0;
1511         }
1512
1513         /* FIXME: if aware of authc/authz mapping, 
1514          * we could use different contexts ... */
1515         switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 
1516                                 &out->bv_val ) )
1517         {
1518         case REWRITE_REGEXEC_OK:
1519                 if ( !BER_BVISNULL( out ) ) {
1520                         char *val = out->bv_val;
1521                         ber_str2bv_x( val, 0, 1, out, ctx );
1522                         if ( val != in->bv_val ) {
1523                                 free( val );
1524                         }
1525                 } else {
1526                         ber_dupbv_x( out, in, ctx );
1527                 }
1528                 Debug( LDAP_DEBUG_ARGS,
1529                         "[rw] %s: \"%s\" -> \"%s\"\n",
1530                         context, in->bv_val, out->bv_val );             
1531                 return 1;
1532                 
1533         case REWRITE_REGEXEC_UNWILLING:
1534         case REWRITE_REGEXEC_ERR:
1535         default:
1536                 return 0;
1537         }
1538
1539 #else /* ! SLAP_AUTH_REWRITE */
1540         char *saslname = in->bv_val;
1541         SaslRegexp_t *reg;
1542         regmatch_t sr_strings[SASLREGEX_REPLACE];       /* strings matching $1,$2 ... */
1543         int i;
1544
1545         memset( out, 0, sizeof( *out ) );
1546
1547         Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1548            saslname, 0, 0 );
1549
1550         if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1551                 return( 0 );
1552         }
1553
1554         /* Match the normalized SASL name to the saslregexp patterns */
1555         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
1556                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
1557                   sr_strings, 0)  == 0 )
1558                         break;
1559         }
1560
1561         if( i >= nSaslRegexp ) return( 0 );
1562
1563         /*
1564          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1565          * replace pattern of the form "x$1y$2z". The returned string needs
1566          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1567          */
1568         slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1569                 sr_strings, saslname, out, ctx );
1570
1571         Debug( LDAP_DEBUG_TRACE,
1572                 "slap_authz_regexp: converted SASL name to %s\n",
1573                 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1574
1575         return( 1 );
1576 #endif /* ! SLAP_AUTH_REWRITE */
1577 }
1578
1579 /* This callback actually does some work...*/
1580 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
1581 {
1582         struct berval *ndn = o->o_callback->sc_private;
1583
1584         if (rs->sr_type != REP_SEARCH) return 0;
1585
1586         /* We only want to be called once */
1587         if ( !BER_BVISNULL( ndn ) ) {
1588                 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
1589                 BER_BVZERO( ndn );
1590
1591                 Debug( LDAP_DEBUG_TRACE,
1592                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
1593                 return -1;
1594         }
1595
1596         ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
1597         return 0;
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 #ifdef LDAP_SCOPE_SUBORDINATE
1974         case LDAP_SCOPE_SUBORDINATE:
1975 #endif
1976                 /* do a search */
1977                 break;
1978
1979         default:
1980                 /* catch unhandled cases (there shouldn't be) */
1981                 assert( 0 );
1982         }
1983
1984         Debug( LDAP_DEBUG_TRACE,
1985                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1986                 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1987
1988         if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1989                 goto FINISHED;
1990         }
1991
1992         /* Must run an internal search. */
1993         if ( op.ors_filter == NULL ) {
1994                 rc = LDAP_FILTER_ERROR;
1995                 goto FINISHED;
1996         }
1997
1998         op.o_hdr = opx->o_hdr;
1999         op.o_tag = LDAP_REQ_SEARCH;
2000         op.o_ndn = opx->o_conn->c_ndn;
2001         op.o_callback = &cb;
2002         slap_op_time( &op.o_time, &op.o_tincr );
2003         op.o_do_not_cache = 1;
2004         op.o_is_auth_check = 1;
2005         op.ors_deref = LDAP_DEREF_NEVER;
2006         op.ors_slimit = 1;
2007         op.ors_tlimit = SLAP_NO_LIMIT;
2008         op.ors_attrs = slap_anlist_no_attrs;
2009         op.ors_attrsonly = 1;
2010         /* use req_ndn as req_dn instead of non-pretty base of uri */
2011         if( !BER_BVISNULL( &base ) ) {
2012                 ch_free( base.bv_val );
2013                 /* just in case... */
2014                 BER_BVZERO( &base );
2015         }
2016         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2017
2018         op.o_bd->be_search( &op, &rs );
2019         
2020 FINISHED:
2021         if( !BER_BVISEMPTY( sasldn ) ) {
2022                 opx->o_conn->c_authz_backend = op.o_bd;
2023         }
2024         if( !BER_BVISNULL( &op.o_req_dn ) ) {
2025                 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2026         }
2027         if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2028                 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2029         }
2030         if( op.ors_filter ) {
2031                 filter_free_x( opx, op.ors_filter );
2032         }
2033         if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2034                 ch_free( op.ors_filterstr.bv_val );
2035         }
2036
2037         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2038                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2039
2040         return;
2041 }
2042
2043
2044 /* Check if a bind can SASL authorize to another identity.
2045  * The DNs should not have the dn: prefix
2046  */
2047
2048 int slap_sasl_authorized( Operation *op,
2049         struct berval *authcDN, struct berval *authzDN )
2050 {
2051         int rc = LDAP_INAPPROPRIATE_AUTH;
2052
2053         /* User binding as anonymous */
2054         if ( authzDN == NULL ) {
2055                 rc = LDAP_SUCCESS;
2056                 goto DONE;
2057         }
2058
2059         Debug( LDAP_DEBUG_TRACE,
2060            "==>slap_sasl_authorized: can %s become %s?\n",
2061                 authcDN->bv_val, authzDN->bv_val, 0 );
2062
2063         /* If person is authorizing to self, succeed */
2064         if ( dn_match( authcDN, authzDN ) ) {
2065                 rc = LDAP_SUCCESS;
2066                 goto DONE;
2067         }
2068
2069         /* Allow the manager to authorize as any DN. */
2070         if( op->o_conn->c_authz_backend &&
2071                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2072         {
2073                 rc = LDAP_SUCCESS;
2074                 goto DONE;
2075         }
2076
2077         /* Check source rules */
2078         if( authz_policy & SASL_AUTHZ_TO ) {
2079                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2080                         slap_schema.si_ad_saslAuthzTo, authcDN );
2081                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2082                         goto DONE;
2083                 }
2084         }
2085
2086         /* Check destination rules */
2087         if( authz_policy & SASL_AUTHZ_FROM ) {
2088                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2089                         slap_schema.si_ad_saslAuthzFrom, authcDN );
2090                 if( rc == LDAP_SUCCESS ) {
2091                         goto DONE;
2092                 }
2093         }
2094
2095         rc = LDAP_INAPPROPRIATE_AUTH;
2096
2097 DONE:
2098
2099         Debug( LDAP_DEBUG_TRACE,
2100                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
2101
2102         return( rc );
2103 }