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