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