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