]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
Sync with HEAD
[openldap] / servers / slapd / saslauthz.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-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 int slap_sasl_rewrite_destroy( void )
1317 {
1318         if ( sasl_rwinfo ) {
1319                 rewrite_info_delete( &sasl_rwinfo );
1320                 sasl_rwinfo = NULL;
1321         }
1322
1323         return 0;
1324 }
1325
1326 int slap_sasl_regexp_rewrite_config(
1327                 const char      *fname,
1328                 int             lineno,
1329                 const char      *match,
1330                 const char      *replace,
1331                 const char      *context )
1332 {
1333         int     rc;
1334         char    *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1335
1336         /* init at first call */
1337         if ( sasl_rwinfo == NULL ) {
1338                 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1339                 char *argvContext[] = { "rewriteContext", NULL, NULL };
1340
1341                 /* initialize rewrite engine */
1342                 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1343
1344                 /* switch on rewrite engine */
1345                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1346                 if (rc != LDAP_SUCCESS) {
1347                         return rc;
1348                 }
1349
1350                 /* create generic authid context */
1351                 argvContext[1] = AUTHID_CONTEXT;
1352                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1353                 if (rc != LDAP_SUCCESS) {
1354                         return rc;
1355                 }
1356         }
1357
1358         argvRule[1] = (char *)match;
1359         argvRule[2] = (char *)replace;
1360         rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1361
1362         return rc;
1363 }
1364 #endif /* SLAP_AUTH_REWRITE */
1365
1366 int slap_sasl_regexp_config( const char *match, const char *replace )
1367 {
1368         int rc;
1369         SaslRegexp_t *reg;
1370
1371         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1372           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1373
1374         reg = &SaslRegexp[nSaslRegexp];
1375
1376         reg->sr_match = ch_strdup( match );
1377         reg->sr_replace = ch_strdup( replace );
1378
1379 #ifdef SLAP_AUTH_REWRITE
1380         rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1381                         match, replace, AUTHID_CONTEXT );
1382         if ( rc == LDAP_SUCCESS ) nSaslRegexp++;
1383         return rc;
1384 #else /* ! SLAP_AUTH_REWRITE */
1385
1386         /* Precompile matching pattern */
1387         rc = regcomp( &reg->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
1388         if ( rc ) {
1389                 Debug( LDAP_DEBUG_ANY,
1390                 "SASL match pattern %s could not be compiled by regexp engine\n",
1391                 reg->sr_match, 0, 0 );
1392
1393 #ifdef ENABLE_REWRITE
1394         /* Dummy block to force symbol references in librewrite */
1395         if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1396                 rewrite_info_init( 0 );
1397         }
1398 #endif
1399                 return( LDAP_OTHER );
1400         }
1401
1402         rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
1403         if ( rc != LDAP_SUCCESS ) return rc;
1404
1405         nSaslRegexp++;
1406         return( LDAP_SUCCESS );
1407 #endif /* ! SLAP_AUTH_REWRITE */
1408 }
1409
1410 void slap_sasl_regexp_unparse( BerVarray *out )
1411 {
1412         int i;
1413         BerVarray bva = NULL;
1414         char ibuf[32], *ptr;
1415         struct berval idx;
1416
1417         if ( !nSaslRegexp ) return;
1418
1419         idx.bv_val = ibuf;
1420         bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1421         BER_BVZERO(bva+nSaslRegexp);
1422         for ( i=0; i<nSaslRegexp; i++ ) {
1423                 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1424                 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1425                         strlen( SaslRegexp[i].sr_replace ) + 5;
1426                 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1427                 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1428                 *ptr++ = '"';
1429                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1430                 ptr = lutil_strcopy( ptr, "\" \"" );
1431                 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1432                 *ptr++ = '"';
1433                 *ptr = '\0';
1434         }
1435         *out = bva;
1436 }
1437
1438 #ifndef SLAP_AUTH_REWRITE
1439 /* Perform replacement on regexp matches */
1440 static void slap_sasl_rx_exp(
1441         const char *rep,
1442         const int *off,
1443         regmatch_t *str,
1444         const char *saslname,
1445         struct berval *out,
1446         void *ctx )
1447 {
1448         int i, n, len, insert;
1449
1450         /* Get the total length of the final URI */
1451
1452         n=1;
1453         len = 0;
1454         while( off[n] >= 0 ) {
1455                 /* Len of next section from replacement string (x,y,z above) */
1456                 len += off[n] - off[n-1] - 2;
1457                 if( off[n+1] < 0)
1458                         break;
1459
1460                 /* Len of string from saslname that matched next $i  (b,d above) */
1461                 i = rep[ off[n] + 1 ]   - '0';
1462                 len += str[i].rm_eo - str[i].rm_so;
1463                 n++;
1464         }
1465         out->bv_val = slap_sl_malloc( len + 1, ctx );
1466         out->bv_len = len;
1467
1468         /* Fill in URI with replace string, replacing $i as we go */
1469         n=1;
1470         insert = 0;
1471         while( off[n] >= 0) {
1472                 /* Paste in next section from replacement string (x,y,z above) */
1473                 len = off[n] - off[n-1] - 2;
1474                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1475                 insert += len;
1476                 if( off[n+1] < 0)
1477                         break;
1478
1479                 /* Paste in string from saslname that matched next $i  (b,d above) */
1480                 i = rep[ off[n] + 1 ]   - '0';
1481                 len = str[i].rm_eo - str[i].rm_so;
1482                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1483                 insert += len;
1484
1485                 n++;
1486         }
1487
1488         out->bv_val[insert] = '\0';
1489 }
1490 #endif /* ! SLAP_AUTH_REWRITE */
1491
1492 /* Take the passed in SASL name and attempt to convert it into an
1493    LDAP URI to find the matching LDAP entry, using the pattern matching
1494    strings given in the saslregexp config file directive(s) */
1495
1496 static int slap_authz_regexp( struct berval *in, struct berval *out,
1497                 int flags, void *ctx )
1498 {
1499 #ifdef SLAP_AUTH_REWRITE
1500         const char      *context = AUTHID_CONTEXT;
1501
1502         if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1503                 return 0;
1504         }
1505
1506         /* FIXME: if aware of authc/authz mapping, 
1507          * we could use different contexts ... */
1508         switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 
1509                                 &out->bv_val ) )
1510         {
1511         case REWRITE_REGEXEC_OK:
1512                 if ( !BER_BVISNULL( out ) ) {
1513                         char *val = out->bv_val;
1514                         ber_str2bv_x( val, 0, 1, out, ctx );
1515                         if ( val != in->bv_val ) {
1516                                 free( val );
1517                         }
1518                 } else {
1519                         ber_dupbv_x( out, in, ctx );
1520                 }
1521                 Debug( LDAP_DEBUG_ARGS,
1522                         "[rw] %s: \"%s\" -> \"%s\"\n",
1523                         context, in->bv_val, out->bv_val );             
1524                 return 1;
1525                 
1526         case REWRITE_REGEXEC_UNWILLING:
1527         case REWRITE_REGEXEC_ERR:
1528         default:
1529                 return 0;
1530         }
1531
1532 #else /* ! SLAP_AUTH_REWRITE */
1533         char *saslname = in->bv_val;
1534         SaslRegexp_t *reg;
1535         regmatch_t sr_strings[SASLREGEX_REPLACE];       /* strings matching $1,$2 ... */
1536         int i;
1537
1538         memset( out, 0, sizeof( *out ) );
1539
1540         Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1541            saslname, 0, 0 );
1542
1543         if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1544                 return( 0 );
1545         }
1546
1547         /* Match the normalized SASL name to the saslregexp patterns */
1548         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
1549                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
1550                   sr_strings, 0)  == 0 )
1551                         break;
1552         }
1553
1554         if( i >= nSaslRegexp ) return( 0 );
1555
1556         /*
1557          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1558          * replace pattern of the form "x$1y$2z". The returned string needs
1559          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1560          */
1561         slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1562                 sr_strings, saslname, out, ctx );
1563
1564         Debug( LDAP_DEBUG_TRACE,
1565                 "slap_authz_regexp: converted SASL name to %s\n",
1566                 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1567
1568         return( 1 );
1569 #endif /* ! SLAP_AUTH_REWRITE */
1570 }
1571
1572 /* This callback actually does some work...*/
1573 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1574 {
1575         struct berval *ndn = op->o_callback->sc_private;
1576
1577         if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1578
1579         /* We only want to be called once */
1580         if ( !BER_BVISNULL( ndn ) ) {
1581                 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1582                 BER_BVZERO( ndn );
1583
1584                 Debug( LDAP_DEBUG_TRACE,
1585                         "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1586                         op->o_log_prefix, 0, 0 );
1587                 return LDAP_OTHER;
1588         }
1589
1590         ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1591         return LDAP_SUCCESS;
1592 }
1593
1594
1595 typedef struct smatch_info {
1596         struct berval *dn;
1597         int match;
1598 } smatch_info;
1599
1600 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1601 {
1602         smatch_info *sm = o->o_callback->sc_private;
1603
1604         if ( rs->sr_type != REP_SEARCH ) {
1605                 if ( rs->sr_err != LDAP_SUCCESS ) {
1606                         sm->match = -1;
1607                 }
1608                 return 0;
1609         }
1610
1611         if ( sm->match == 1 ) {
1612                 sm->match = -1;
1613                 return 0;
1614         }
1615
1616         if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1617                 sm->match = 1;
1618
1619         } else {
1620                 sm->match = -1;
1621         }
1622
1623         return 0;
1624 }
1625
1626 int
1627 slap_sasl_matches( Operation *op, BerVarray rules,
1628                 struct berval *assertDN, struct berval *authc )
1629 {
1630         int     rc = LDAP_INAPPROPRIATE_AUTH;
1631
1632         if ( rules != NULL ) {
1633                 int     i;
1634
1635                 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1636                         rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1637                         if ( rc == LDAP_SUCCESS ) break;
1638                 }
1639         }
1640         
1641         return rc;
1642 }
1643
1644 /*
1645  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1646  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1647  * the rule must be used as an internal search for entries. If that search
1648  * returns the *assertDN entry, the match is successful.
1649  *
1650  * The assertDN should not have the dn: prefix
1651  */
1652
1653 static int
1654 slap_sasl_match( Operation *opx, struct berval *rule,
1655         struct berval *assertDN, struct berval *authc )
1656 {
1657         int rc; 
1658         regex_t reg;
1659         smatch_info sm;
1660         slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1661         Operation op = {0};
1662         SlapReply rs = {REP_RESULT};
1663         struct berval base = BER_BVNULL;
1664
1665         sm.dn = assertDN;
1666         sm.match = 0;
1667         cb.sc_private = &sm;
1668
1669         Debug( LDAP_DEBUG_TRACE,
1670            "===>slap_sasl_match: comparing DN %s to rule %s\n",
1671                 assertDN->bv_val, rule->bv_val, 0 );
1672
1673         /* NOTE: don't normalize rule if authz syntax is enabled */
1674         rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1675                 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
1676
1677         if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1678
1679         switch ( op.ors_scope ) {
1680         case LDAP_X_SCOPE_EXACT:
1681 exact_match:
1682                 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1683                         rc = LDAP_SUCCESS;
1684                 } else {
1685                         rc = LDAP_INAPPROPRIATE_AUTH;
1686                 }
1687                 goto CONCLUDED;
1688
1689         case LDAP_X_SCOPE_CHILDREN:
1690         case LDAP_X_SCOPE_SUBTREE:
1691         case LDAP_X_SCOPE_ONELEVEL:
1692         {
1693                 int     d = assertDN->bv_len - op.o_req_ndn.bv_len;
1694
1695                 rc = LDAP_INAPPROPRIATE_AUTH;
1696
1697                 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1698                         goto exact_match;
1699
1700                 } else if ( d > 0 ) {
1701                         struct berval bv;
1702
1703                         /* leave room for at least one char of attributeType,
1704                          * one for '=' and one for ',' */
1705                         if ( d < 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 == 1) {
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 }