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