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