]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
ITS#3226: Clear attribute flags after schema_check failed
[openldap] / servers / slapd / saslauthz.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2004 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
21 #include <ac/stdlib.h>
22 #include <ac/string.h>
23 #include <ac/ctype.h>
24
25 #include "slap.h"
26
27 #include <limits.h>
28
29 #include <ldap_pvt.h>
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 int authz_policy = SASL_AUTHZ_NONE;
92
93 static
94 int slap_sasl_match( Operation *opx, struct berval *rule,
95         struct berval *assertDN, struct berval *authc );
96
97 int slap_sasl_setpolicy( const char *arg )
98 {
99         int rc = LDAP_SUCCESS;
100
101         if ( strcasecmp( arg, "none" ) == 0 ) {
102                 authz_policy = SASL_AUTHZ_NONE;
103         } else if ( strcasecmp( arg, "from" ) == 0 ) {
104                 authz_policy = SASL_AUTHZ_FROM;
105         } else if ( strcasecmp( arg, "to" ) == 0 ) {
106                 authz_policy = SASL_AUTHZ_TO;
107         } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
108                 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
109         } else if ( strcasecmp( arg, "all" ) == 0 ) {
110                 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
111         } else {
112                 rc = LDAP_OTHER;
113         }
114         return rc;
115 }
116
117 int slap_parse_user( struct berval *id, struct berval *user,
118                 struct berval *realm, struct berval *mech )
119 {
120         char    u;
121         
122         assert( id );
123         assert( !BER_BVISNULL( id ) );
124         assert( user );
125         assert( realm );
126         assert( mech );
127
128         u = id->bv_val[ 0 ];
129         
130         if ( u != 'u' && u != 'U' ) {
131                 /* called with something other than u: */
132                 return LDAP_PROTOCOL_ERROR;
133         }
134
135         /* uauthzid form:
136          *              u[.mech[/realm]]:user
137          */
138         
139         user->bv_val = strchr( id->bv_val, ':' );
140         if ( BER_BVISNULL( user ) ) {
141                 return LDAP_PROTOCOL_ERROR;
142         }
143         user->bv_val[ 0 ] = '\0';
144         user->bv_val++;
145         user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
146
147         mech->bv_val = strchr( id->bv_val, '.' );
148         if ( !BER_BVISNULL( mech ) ) {
149                 mech->bv_val[ 0 ] = '\0';
150                 mech->bv_val++;
151
152                 realm->bv_val = strchr( mech->bv_val, '/' );
153
154                 if ( !BER_BVISNULL( realm ) ) {
155                         realm->bv_val[ 0 ] = '\0';
156                         realm->bv_val++;
157                         mech->bv_len = realm->bv_val - mech->bv_val - 1;
158                         realm->bv_len = user->bv_val - realm->bv_val - 1;
159                 } else {
160                         mech->bv_len = user->bv_val - mech->bv_val - 1;
161                 }
162
163         } else {
164                 BER_BVZERO( realm );
165         }
166
167         if ( id->bv_val[ 1 ] != '\0' ) {
168                 return LDAP_PROTOCOL_ERROR;
169         }
170
171         if ( !BER_BVISNULL( mech ) ) {
172                 assert( mech->bv_val == id->bv_val + 2 );
173
174                 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
175                 mech->bv_val -= 2;
176         }
177
178         if ( !BER_BVISNULL( realm ) ) {
179                 assert( realm->bv_val >= id->bv_val + 2 );
180
181                 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
182                 realm->bv_val -= 2;
183         }
184
185         /* leave "u:" before user */
186         user->bv_val -= 2;
187         user->bv_len += 2;
188         user->bv_val[ 0 ] = u;
189         user->bv_val[ 1 ] = ':';
190
191         return LDAP_SUCCESS;
192 }
193
194 static int slap_parseURI( Operation *op, struct berval *uri,
195         struct berval *base, struct berval *nbase,
196         int *scope, Filter **filter, struct berval *fstr )
197 {
198         struct berval bv;
199         int rc;
200         LDAPURLDesc *ludp;
201
202         assert( uri != NULL && !BER_BVISNULL( uri ) );
203         BER_BVZERO( base );
204         BER_BVZERO( nbase );
205         BER_BVZERO( fstr );
206         *scope = -1;
207         *filter = NULL;
208
209 #ifdef NEW_LOGGING
210         LDAP_LOG( TRANSPORT, ENTRY, 
211                 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
212 #else
213         Debug( LDAP_DEBUG_TRACE,
214                 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
215 #endif
216
217         rc = LDAP_PROTOCOL_ERROR;
218         /*
219          * dn[.<dnstyle>]:<dnpattern>
220          * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
221          *
222          * <dnstyle> defaults to "exact"
223          * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
224          */
225         if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
226                 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
227
228                 if ( bv.bv_val[ 0 ] == '.' ) {
229                         bv.bv_val++;
230
231                         if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
232                                 bv.bv_val += STRLENOF( "exact:" );
233                                 *scope = LDAP_X_SCOPE_EXACT;
234
235                         } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
236                                 bv.bv_val += STRLENOF( "regex:" );
237                                 *scope = LDAP_X_SCOPE_REGEX;
238
239                         } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
240                                 bv.bv_val += STRLENOF( "children:" );
241                                 *scope = LDAP_X_SCOPE_CHILDREN;
242
243                         } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
244                                 bv.bv_val += STRLENOF( "subtree:" );
245                                 *scope = LDAP_X_SCOPE_SUBTREE;
246
247                         } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
248                                 bv.bv_val += STRLENOF( "onelevel:" );
249                                 *scope = LDAP_X_SCOPE_ONELEVEL;
250
251                         } else {
252                                 return LDAP_PROTOCOL_ERROR;
253                         }
254
255                 } else {
256                         if ( bv.bv_val[ 0 ] != ':' ) {
257                                 return LDAP_PROTOCOL_ERROR;
258                         }
259                         *scope = LDAP_X_SCOPE_EXACT;
260                         bv.bv_val++;
261                 }
262
263                 bv.bv_val += strspn( bv.bv_val, " " );
264                 /* jump here in case no type specification was present
265                  * and uri was not an URI... HEADS-UP: assuming EXACT */
266 is_dn:          bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
267
268                 /* a single '*' means any DN without using regexes */
269                 if ( ber_bvccmp( &bv, '*' ) ) {
270                         *scope = LDAP_X_SCOPE_USERS;
271                 }
272
273                 switch ( *scope ) {
274                 case LDAP_X_SCOPE_EXACT:
275                 case LDAP_X_SCOPE_CHILDREN:
276                 case LDAP_X_SCOPE_SUBTREE:
277                 case LDAP_X_SCOPE_ONELEVEL:
278                         rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
279                         if( rc != LDAP_SUCCESS ) {
280                                 *scope = -1;
281                         }
282                         break;
283
284                 case LDAP_X_SCOPE_REGEX:
285                         ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
286
287                 case LDAP_X_SCOPE_USERS:
288                         rc = LDAP_SUCCESS;
289                         break;
290
291                 default:
292                         *scope = -1;
293                         break;
294                 }
295
296                 return rc;
297
298         /*
299          * u:<uid>
300          */
301         } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
302                         && ( uri->bv_val[ 1 ] == ':' 
303                                 || uri->bv_val[ 1 ] == '/' 
304                                 || uri->bv_val[ 1 ] == '.' ) )
305         {
306                 Connection      c = *op->o_conn;
307                 char            buf[ SLAP_LDAPDN_MAXLEN ];
308                 struct berval   id,
309                                 user = BER_BVNULL,
310                                 realm = BER_BVNULL,
311                                 mech = BER_BVNULL;
312
313                 if ( sizeof( buf ) <= uri->bv_len ) {
314                         return LDAP_INVALID_SYNTAX;
315                 }
316
317                 id.bv_len = uri->bv_len;
318                 id.bv_val = buf;
319                 strncpy( buf, uri->bv_val, sizeof( buf ) );
320
321                 rc = slap_parse_user( &id, &user, &realm, &mech );
322                 if ( rc != LDAP_SUCCESS ) {
323                         return rc;
324                 }
325
326                 if ( !BER_BVISNULL( &mech ) ) {
327                         c.c_sasl_bind_mech = mech;
328                 } else {
329                         BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
330                 }
331                 
332                 rc = slap_sasl_getdn( &c, op, &user,
333                                 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
334
335                 if ( rc == LDAP_SUCCESS ) {
336                         *scope = LDAP_X_SCOPE_EXACT;
337                 }
338
339                 return rc;
340
341         /*
342          * group[/<groupoc>[/<groupat>]]:<groupdn>
343          *
344          * groupoc defaults to "groupOfNames"
345          * groupat defaults to "member"
346          * 
347          * <groupdn> must pass DN normalization
348          */
349         } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
350         {
351                 struct berval   group_dn = BER_BVNULL,
352                                 group_oc = BER_BVNULL,
353                                 member_at = BER_BVNULL;
354                 char            *tmp;
355
356                 bv.bv_val = uri->bv_val + STRLENOF( "group" );
357                 group_dn.bv_val = strchr( bv.bv_val, ':' );
358                 if ( group_dn.bv_val == NULL ) {
359                         /* last chance: assume it's a(n exact) DN ... */
360                         bv.bv_val = uri->bv_val;
361                         *scope = LDAP_X_SCOPE_EXACT;
362                         goto is_dn;
363                 }
364                 
365                 if ( bv.bv_val[ 0 ] == '/' ) {
366                         group_oc.bv_val = &bv.bv_val[ 1 ];
367
368                         member_at.bv_val = strchr( group_oc.bv_val, '/' );
369                         if ( member_at.bv_val ) {
370                                 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
371                                 member_at.bv_val++;
372                                 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
373
374                         } else {
375                                 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
376                                 BER_BVSTR( &member_at, "member" );
377                         }
378
379                 } else {
380                         BER_BVSTR( &group_oc, "groupOfNames" );
381                 }
382                 group_dn.bv_val++;
383                 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
384
385                 rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
386                 if ( rc != LDAP_SUCCESS ) {
387                         *scope = -1;
388                         return rc;
389                 }
390                 *scope = LDAP_X_SCOPE_GROUP;
391
392                 /* FIXME: caller needs to add value of member attribute
393                  * and close brackets twice */
394                 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
395                         + group_oc.bv_len + member_at.bv_len;
396                 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
397
398                 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
399                                 STRLENOF( "(&(objectClass=" /* )) */ ) );
400                 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
401                 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
402                                 STRLENOF( /* ( */ ")(" /* ) */ ) );
403                 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
404                 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
405
406                 return rc;
407         }
408
409         /*
410          * ldap:///<base>??<scope>?<filter>
411          * <scope> ::= {base|one|subtree}
412          *
413          * <scope> defaults to "base"
414          * <base> must pass DN normalization
415          * <filter> must pass str2filter()
416          */
417         rc = ldap_url_parse( uri->bv_val, &ludp );
418         if ( rc == LDAP_URL_ERR_BADSCHEME ) {
419                 /*
420                  * last chance: assume it's a(n exact) DN ...
421                  *
422                  * NOTE: must pass DN normalization
423                  */
424                 bv.bv_val = uri->bv_val;
425                 *scope = LDAP_X_SCOPE_EXACT;
426                 goto is_dn;
427         }
428
429         if ( rc != LDAP_URL_SUCCESS ) {
430                 return LDAP_PROTOCOL_ERROR;
431         }
432
433         if (( ludp->lud_host && *ludp->lud_host )
434                 || ludp->lud_attrs || ludp->lud_exts )
435         {
436                 /* host part must be empty */
437                 /* attrs and extensions parts must be empty */
438                 rc = LDAP_PROTOCOL_ERROR;
439                 goto done;
440         }
441
442         /* Grab the scope */
443         *scope = ludp->lud_scope;
444
445         /* Grab the filter */
446         if ( ludp->lud_filter ) {
447                 *filter = str2filter_x( op, ludp->lud_filter );
448                 if ( *filter == NULL ) {
449                         rc = LDAP_PROTOCOL_ERROR;
450                         goto done;
451                 }
452                 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
453         }
454
455         /* Grab the searchbase */
456         ber_str2bv( ludp->lud_dn, 0, 0, base );
457         rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
458
459 done:
460         if( rc != LDAP_SUCCESS ) {
461                 if( *filter ) filter_free_x( op, *filter );
462                 BER_BVZERO( base );
463                 BER_BVZERO( fstr );
464         } else {
465                 /* Don't free these, return them to caller */
466                 ludp->lud_filter = NULL;
467                 ludp->lud_dn = NULL;
468         }
469
470         ldap_free_urldesc( ludp );
471         return( rc );
472 }
473
474 static int slap_sasl_rx_off(char *rep, int *off)
475 {
476         const char *c;
477         int n;
478
479         /* Precompile replace pattern. Find the $<n> placeholders */
480         off[0] = -2;
481         n = 1;
482         for ( c = rep;   *c;  c++ ) {
483                 if ( *c == '\\' && c[1] ) {
484                         c++;
485                         continue;
486                 }
487                 if ( *c == '$' ) {
488                         if ( n == SASLREGEX_REPLACE ) {
489 #ifdef NEW_LOGGING
490                                 LDAP_LOG( TRANSPORT, ERR, 
491                                         "slap_sasl_rx_off: \"%s\" has too many $n "
492                                         "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0  );
493 #else
494                                 Debug( LDAP_DEBUG_ANY,
495                                         "SASL replace pattern %s has too many $n "
496                                                 "placeholders (max %d)\n",
497                                         rep, SASLREGEX_REPLACE, 0 );
498 #endif
499
500                                 return( LDAP_OTHER );
501                         }
502                         off[n] = c - rep;
503                         n++;
504                 }
505         }
506
507         /* Final placeholder, after the last $n */
508         off[n] = c - rep;
509         n++;
510         off[n] = -1;
511         return( LDAP_SUCCESS );
512 }
513
514 #ifdef SLAP_AUTH_REWRITE
515 int slap_sasl_rewrite_config( 
516                 const char      *fname,
517                 int             lineno,
518                 int             argc,
519                 char            **argv
520 )
521 {
522         int     rc;
523         char    *savearg0;
524
525         /* init at first call */
526         if ( sasl_rwinfo == NULL ) {
527                 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
528         }
529
530         /* strip "authid-" prefix for parsing */
531         savearg0 = argv[0];
532         argv[0] += STRLENOF( "authid-" );
533         rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
534         argv[0] = savearg0;
535
536         return rc;
537 }
538
539 int slap_sasl_rewrite_destroy( void )
540 {
541         if ( sasl_rwinfo ) {
542                 rewrite_info_delete( &sasl_rwinfo );
543                 sasl_rwinfo = NULL;
544         }
545
546         return 0;
547 }
548
549 int slap_sasl_regexp_rewrite_config(
550                 const char      *fname,
551                 int             lineno,
552                 const char      *match,
553                 const char      *replace,
554                 const char      *context )
555 {
556         int     rc;
557         char    *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
558
559         /* init at first call */
560         if ( sasl_rwinfo == NULL ) {
561                 char *argvEngine[] = { "rewriteEngine", "on", NULL };
562                 char *argvContext[] = { "rewriteContext", NULL, NULL };
563
564                 /* initialize rewrite engine */
565                 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
566
567                 /* switch on rewrite engine */
568                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
569                 if (rc != LDAP_SUCCESS) {
570                         return rc;
571                 }
572
573                 /* create generic authid context */
574                 argvContext[1] = AUTHID_CONTEXT;
575                 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
576                 if (rc != LDAP_SUCCESS) {
577                         return rc;
578                 }
579         }
580
581         argvRule[1] = (char *)match;
582         argvRule[2] = (char *)replace;
583         rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
584
585         return rc;
586 }
587 #endif /* SLAP_AUTH_REWRITE */
588
589 int slap_sasl_regexp_config( const char *match, const char *replace )
590 {
591 #ifdef SLAP_AUTH_REWRITE
592         return slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
593                         match, replace, AUTHID_CONTEXT );
594 #else /* ! SLAP_AUTH_REWRITE */
595         int rc;
596         SaslRegexp_t *reg;
597
598         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
599           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
600
601         reg = &SaslRegexp[nSaslRegexp];
602
603         reg->sr_match = ch_strdup( match );
604         reg->sr_replace = ch_strdup( replace );
605
606         /* Precompile matching pattern */
607         rc = regcomp( &reg->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
608         if ( rc ) {
609 #ifdef NEW_LOGGING
610                 LDAP_LOG( TRANSPORT, ERR, 
611                         "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
612                         reg->sr_match, 0, 0 );
613 #else
614                 Debug( LDAP_DEBUG_ANY,
615                 "SASL match pattern %s could not be compiled by regexp engine\n",
616                 reg->sr_match, 0, 0 );
617 #endif
618
619                 return( LDAP_OTHER );
620         }
621
622         rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
623         if ( rc != LDAP_SUCCESS ) return rc;
624
625         nSaslRegexp++;
626         return( LDAP_SUCCESS );
627 #endif /* ! SLAP_AUTH_REWRITE */
628 }
629
630 /* Perform replacement on regexp matches */
631 static void slap_sasl_rx_exp(
632         const char *rep,
633         const int *off,
634         regmatch_t *str,
635         const char *saslname,
636         struct berval *out,
637         void *ctx )
638 {
639         int i, n, len, insert;
640
641         /* Get the total length of the final URI */
642
643         n=1;
644         len = 0;
645         while( off[n] >= 0 ) {
646                 /* Len of next section from replacement string (x,y,z above) */
647                 len += off[n] - off[n-1] - 2;
648                 if( off[n+1] < 0)
649                         break;
650
651                 /* Len of string from saslname that matched next $i  (b,d above) */
652                 i = rep[ off[n] + 1 ]   - '0';
653                 len += str[i].rm_eo - str[i].rm_so;
654                 n++;
655         }
656         out->bv_val = slap_sl_malloc( len + 1, ctx );
657         out->bv_len = len;
658
659         /* Fill in URI with replace string, replacing $i as we go */
660         n=1;
661         insert = 0;
662         while( off[n] >= 0) {
663                 /* Paste in next section from replacement string (x,y,z above) */
664                 len = off[n] - off[n-1] - 2;
665                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
666                 insert += len;
667                 if( off[n+1] < 0)
668                         break;
669
670                 /* Paste in string from saslname that matched next $i  (b,d above) */
671                 i = rep[ off[n] + 1 ]   - '0';
672                 len = str[i].rm_eo - str[i].rm_so;
673                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
674                 insert += len;
675
676                 n++;
677         }
678
679         out->bv_val[insert] = '\0';
680 }
681
682 /* Take the passed in SASL name and attempt to convert it into an
683    LDAP URI to find the matching LDAP entry, using the pattern matching
684    strings given in the saslregexp config file directive(s) */
685
686 static int slap_authz_regexp( struct berval *in, struct berval *out,
687                 int flags, void *ctx )
688 {
689 #ifdef SLAP_AUTH_REWRITE
690         const char      *context = AUTHID_CONTEXT;
691
692         if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
693                 return 0;
694         }
695
696         /* FIXME: if aware of authc/authz mapping, 
697          * we could use different contexts ... */
698         switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL, 
699                                 &out->bv_val ) )
700         {
701         case REWRITE_REGEXEC_OK:
702                 if ( !BER_BVISNULL( out ) ) {
703                         char *val = out->bv_val;
704                         ber_str2bv_x( val, 0, 1, out, ctx );
705                         free( val );
706                 } else {
707                         ber_dupbv_x( out, in, ctx );
708                 }
709 #ifdef NEW_LOGGING
710                 LDAP_LOG( BACK_LDAP, DETAIL1, 
711                         "[rw] %s: \"%s\" -> \"%s\"\n",
712                         context, in->bv_val, out->bv_val );             
713 #else /* !NEW_LOGGING */
714                 Debug( LDAP_DEBUG_ARGS,
715                         "[rw] %s: \"%s\" -> \"%s\"\n",
716                         context, in->bv_val, out->bv_val );             
717 #endif /* !NEW_LOGGING */
718                 return 1;
719                 
720         case REWRITE_REGEXEC_UNWILLING:
721         case REWRITE_REGEXEC_ERR:
722         default:
723                 return 0;
724         }
725
726 #else /* ! SLAP_AUTH_REWRITE */
727         char *saslname = in->bv_val;
728         SaslRegexp_t *reg;
729         regmatch_t sr_strings[SASLREGEX_REPLACE];       /* strings matching $1,$2 ... */
730         int i;
731
732         memset( out, 0, sizeof( *out ) );
733
734 #ifdef NEW_LOGGING
735         LDAP_LOG( TRANSPORT, ENTRY, 
736                 "slap_authz_regexp: converting SASL name %s\n", saslname, 0, 0 );
737 #else
738         Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
739            saslname, 0, 0 );
740 #endif
741
742         if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
743                 return( 0 );
744         }
745
746         /* Match the normalized SASL name to the saslregexp patterns */
747         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
748                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
749                   sr_strings, 0)  == 0 )
750                         break;
751         }
752
753         if( i >= nSaslRegexp ) return( 0 );
754
755         /*
756          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
757          * replace pattern of the form "x$1y$2z". The returned string needs
758          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
759          */
760         slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
761                 sr_strings, saslname, out, ctx );
762
763 #ifdef NEW_LOGGING
764         LDAP_LOG( TRANSPORT, ENTRY, 
765                 "slap_authz_regexp: converted SASL name to %s\n",
766                 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
767 #else
768         Debug( LDAP_DEBUG_TRACE,
769                 "slap_authz_regexp: converted SASL name to %s\n",
770                 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
771 #endif
772
773         return( 1 );
774 #endif /* ! SLAP_AUTH_REWRITE */
775 }
776
777 /* This callback actually does some work...*/
778 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
779 {
780         struct berval *ndn = o->o_callback->sc_private;
781
782         if (rs->sr_type != REP_SEARCH) return 0;
783
784         /* We only want to be called once */
785         if ( !BER_BVISNULL( ndn ) ) {
786                 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
787                 BER_BVZERO( ndn );
788
789 #ifdef NEW_LOGGING
790                 LDAP_LOG( TRANSPORT, DETAIL1,
791                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
792 #else
793                 Debug( LDAP_DEBUG_TRACE,
794                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
795 #endif
796                 return -1;
797         }
798
799         ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
800         return 0;
801 }
802
803
804 typedef struct smatch_info {
805         struct berval *dn;
806         int match;
807 } smatch_info;
808
809 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
810 {
811         smatch_info *sm = o->o_callback->sc_private;
812
813         if ( rs->sr_type != REP_SEARCH ) {
814                 if ( rs->sr_err != LDAP_SUCCESS ) {
815                         sm->match = -1;
816                 }
817                 return 0;
818         }
819
820         if ( sm->match == 1 ) {
821                 sm->match = -1;
822                 return 0;
823         }
824
825         if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
826                 sm->match = 1;
827
828         } else {
829                 sm->match = -1;
830         }
831
832         return 0;
833 }
834
835 int
836 slap_sasl_matches( Operation *op, BerVarray rules,
837                 struct berval *assertDN, struct berval *authc )
838 {
839         int     rc = LDAP_INAPPROPRIATE_AUTH;
840
841         if ( rules != NULL ) {
842                 int     i;
843
844                 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
845                         rc = slap_sasl_match( op, &rules[i], assertDN, authc );
846                         if ( rc == LDAP_SUCCESS ) break;
847                 }
848         }
849         
850         return rc;
851 }
852
853 /*
854  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
855  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
856  * the rule must be used as an internal search for entries. If that search
857  * returns the *assertDN entry, the match is successful.
858  *
859  * The assertDN should not have the dn: prefix
860  */
861
862 static
863 int slap_sasl_match( Operation *opx, struct berval *rule,
864         struct berval *assertDN, struct berval *authc )
865 {
866         int rc; 
867         regex_t reg;
868         smatch_info sm;
869         slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
870         Operation op = {0};
871         SlapReply rs = {REP_RESULT};
872
873         sm.dn = assertDN;
874         sm.match = 0;
875         cb.sc_private = &sm;
876
877 #ifdef NEW_LOGGING
878         LDAP_LOG( TRANSPORT, ENTRY, 
879                 "slap_sasl_match: comparing DN %s to rule %s\n", 
880                 assertDN->bv_val, rule->bv_val,0 );
881 #else
882         Debug( LDAP_DEBUG_TRACE,
883            "===>slap_sasl_match: comparing DN %s to rule %s\n",
884                 assertDN->bv_val, rule->bv_val, 0 );
885 #endif
886
887         rc = slap_parseURI( opx, rule, &op.o_req_dn,
888                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
889                 &op.ors_filterstr );
890         if( rc != LDAP_SUCCESS ) goto CONCLUDED;
891
892         switch ( op.oq_search.rs_scope ) {
893         case LDAP_X_SCOPE_EXACT:
894 exact_match:
895                 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
896                         rc = LDAP_SUCCESS;
897                 } else {
898                         rc = LDAP_INAPPROPRIATE_AUTH;
899                 }
900                 goto CONCLUDED;
901
902         case LDAP_X_SCOPE_CHILDREN:
903         case LDAP_X_SCOPE_SUBTREE:
904         case LDAP_X_SCOPE_ONELEVEL:
905         {
906                 int     d = assertDN->bv_len - op.o_req_ndn.bv_len;
907
908                 rc = LDAP_INAPPROPRIATE_AUTH;
909
910                 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
911                         goto exact_match;
912
913                 } else if ( d > 0 ) {
914                         struct berval bv;
915
916                         /* leave room for at least one char of attributeType,
917                          * one for '=' and one for ',' */
918                         if ( d < STRLENOF( "x=,") ) {
919                                 goto CONCLUDED;
920                         }
921
922                         bv.bv_len = op.o_req_ndn.bv_len;
923                         bv.bv_val = assertDN->bv_val + d;
924
925                         if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
926                                 switch ( op.oq_search.rs_scope ) {
927                                 case LDAP_X_SCOPE_SUBTREE:
928                                 case LDAP_X_SCOPE_CHILDREN:
929                                         rc = LDAP_SUCCESS;
930                                         break;
931
932                                 case LDAP_X_SCOPE_ONELEVEL:
933                                 {
934                                         struct berval   pdn;
935
936                                         dnParent( assertDN, &pdn );
937                                         /* the common portion of the DN
938                                          * already matches, so only check
939                                          * if parent DN of assertedDN 
940                                          * is all the pattern */
941                                         if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
942                                                 rc = LDAP_SUCCESS;
943                                         }
944                                         break;
945                                 }
946                                 default:
947                                         /* at present, impossible */
948                                         assert( 0 );
949                                 }
950                         }
951                 }
952                 goto CONCLUDED;
953         }
954
955         case LDAP_X_SCOPE_REGEX:
956                 rc = regcomp(&reg, op.o_req_ndn.bv_val,
957                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
958                 if ( rc == 0 ) {
959                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
960                         regfree( &reg );
961                 }
962                 if ( rc == 0 ) {
963                         rc = LDAP_SUCCESS;
964                 } else {
965                         rc = LDAP_INAPPROPRIATE_AUTH;
966                 }
967                 goto CONCLUDED;
968
969         case LDAP_X_SCOPE_GROUP: {
970                 char    *tmp;
971
972                 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
973                  * we need to append the <assertDN> so that the <group_dn> is searched
974                  * with scope "base", and the filter ensures that <assertDN> is
975                  * member of the group */
976                 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
977                         assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
978                 if ( tmp == NULL ) {
979                         rc = LDAP_NO_MEMORY;
980                         goto CONCLUDED;
981                 }
982                 op.ors_filterstr.bv_val = tmp;
983                 
984                 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
985                 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
986
987                 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
988                 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
989                 if ( op.ors_filter == NULL ) {
990                         rc = LDAP_PROTOCOL_ERROR;
991                         goto CONCLUDED;
992                 }
993                 op.ors_scope = LDAP_SCOPE_BASE;
994
995                 /* hijack match DN: use that of the group instead of the assertDN;
996                  * assertDN is now in the filter */
997                 sm.dn = &op.o_req_ndn;
998
999                 /* do the search */
1000                 break;
1001                 }
1002
1003         case LDAP_X_SCOPE_USERS:
1004                 if ( !BER_BVISEMPTY( assertDN ) ) {
1005                         rc = LDAP_SUCCESS;
1006                 } else {
1007                         rc = LDAP_INAPPROPRIATE_AUTH;
1008                 }
1009                 goto CONCLUDED;
1010
1011         default:
1012                 break;
1013         }
1014
1015         /* Must run an internal search. */
1016         if ( op.oq_search.rs_filter == NULL ) {
1017                 rc = LDAP_FILTER_ERROR;
1018                 goto CONCLUDED;
1019         }
1020
1021 #ifdef NEW_LOGGING
1022         LDAP_LOG( TRANSPORT, DETAIL1, 
1023                 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1024                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1025 #else
1026         Debug( LDAP_DEBUG_TRACE,
1027            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1028            op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1029 #endif
1030
1031         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1032         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1033                 rc = LDAP_INAPPROPRIATE_AUTH;
1034                 goto CONCLUDED;
1035         }
1036
1037         op.o_tag = LDAP_REQ_SEARCH;
1038         op.o_protocol = LDAP_VERSION3;
1039         op.o_ndn = *authc;
1040         op.o_callback = &cb;
1041         op.o_time = slap_get_time();
1042         op.o_do_not_cache = 1;
1043         op.o_is_auth_check = 1;
1044         op.o_threadctx = opx->o_threadctx;
1045         op.o_tmpmemctx = opx->o_tmpmemctx;
1046         op.o_tmpmfuncs = opx->o_tmpmfuncs;
1047 #ifdef LDAP_SLAPI
1048         op.o_pb = opx->o_pb;
1049 #endif
1050         op.o_conn = opx->o_conn;
1051         op.o_connid = opx->o_connid;
1052         /* use req_ndn as req_dn instead of non-pretty base of uri */
1053         if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
1054         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1055         op.oq_search.rs_slimit = 1;
1056         op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
1057         op.o_sync_slog_size = -1;
1058
1059         op.o_bd->be_search( &op, &rs );
1060
1061         if (sm.match == 1) {
1062                 rc = LDAP_SUCCESS;
1063         } else {
1064                 rc = LDAP_INAPPROPRIATE_AUTH;
1065         }
1066
1067 CONCLUDED:
1068         if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1069         if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1070         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
1071         if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1072
1073 #ifdef NEW_LOGGING
1074         LDAP_LOG( TRANSPORT, ENTRY, 
1075                 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
1076 #else
1077         Debug( LDAP_DEBUG_TRACE,
1078            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1079 #endif
1080
1081         return( rc );
1082 }
1083
1084
1085 /*
1086  * This function answers the question, "Can this ID authorize to that ID?",
1087  * based on authorization rules. The rules are stored in the *searchDN, in the
1088  * attribute named by *attr. If any of those rules map to the *assertDN, the
1089  * authorization is approved.
1090  *
1091  * The DNs should not have the dn: prefix
1092  */
1093 static int
1094 slap_sasl_check_authz( Operation *op,
1095         struct berval *searchDN,
1096         struct berval *assertDN,
1097         AttributeDescription *ad,
1098         struct berval *authc )
1099 {
1100         int i, rc;
1101         BerVarray vals = NULL;
1102
1103 #ifdef NEW_LOGGING
1104         LDAP_LOG( TRANSPORT, ENTRY, 
1105                 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
1106             assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1107 #else
1108         Debug( LDAP_DEBUG_TRACE,
1109            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1110            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1111 #endif
1112
1113         rc = backend_attribute( op, NULL, searchDN, ad, &vals );
1114         if( rc != LDAP_SUCCESS ) goto COMPLETE;
1115
1116         /* Check if the *assertDN matches any *vals */
1117         rc = slap_sasl_matches( op, vals, assertDN, authc );
1118
1119 COMPLETE:
1120         if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1121
1122 #ifdef NEW_LOGGING
1123         LDAP_LOG( TRANSPORT, RESULTS, 
1124                 "slap_sasl_check_authz: %s check returning %s\n", 
1125                 ad->ad_cname.bv_val, rc, 0 );
1126 #else
1127         Debug( LDAP_DEBUG_TRACE,
1128            "<==slap_sasl_check_authz: %s check returning %d\n",
1129                 ad->ad_cname.bv_val, rc, 0);
1130 #endif
1131
1132         return( rc );
1133 }
1134
1135 /*
1136  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1137  * return the LDAP DN to which it matches. The SASL regexp rules in the config
1138  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1139  * search with scope=base), just return the URI (or its searchbase). Otherwise
1140  * an internal search must be done, and if that search returns exactly one
1141  * entry, return the DN of that one entry.
1142  */
1143 void slap_sasl2dn( Operation *opx,
1144         struct berval *saslname, struct berval *sasldn, int flags )
1145 {
1146         int rc;
1147         slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1148         Operation op = {0};
1149         SlapReply rs = {REP_RESULT};
1150         struct berval regout = BER_BVNULL;
1151
1152 #ifdef NEW_LOGGING
1153         LDAP_LOG( TRANSPORT, ENTRY, 
1154                 "slap_sasl2dn: converting SASL name %s to DN.\n",
1155                 saslname->bv_val, 0, 0 );
1156 #else
1157         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1158                 "converting SASL name %s to a DN\n",
1159                 saslname->bv_val, 0,0 );
1160 #endif
1161
1162         sasldn->bv_val = NULL;
1163         sasldn->bv_len = 0;
1164         cb.sc_private = sasldn;
1165
1166         /* Convert the SASL name into a minimal URI */
1167         if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
1168                 goto FINISHED;
1169         }
1170
1171         rc = slap_parseURI( opx, &regout, &op.o_req_dn,
1172                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
1173                 &op.ors_filterstr );
1174         if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1175         if ( rc != LDAP_SUCCESS ) {
1176                 goto FINISHED;
1177         }
1178
1179         /* Must do an internal search */
1180         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1181
1182         switch ( op.oq_search.rs_scope ) {
1183         case LDAP_X_SCOPE_EXACT:
1184                 *sasldn = op.o_req_ndn;
1185                 BER_BVZERO( &op.o_req_ndn );
1186                 /* intentionally continue to next case */
1187
1188         case LDAP_X_SCOPE_REGEX:
1189         case LDAP_X_SCOPE_SUBTREE:
1190         case LDAP_X_SCOPE_CHILDREN:
1191         case LDAP_X_SCOPE_ONELEVEL:
1192         case LDAP_X_SCOPE_GROUP:
1193         case LDAP_X_SCOPE_USERS:
1194                 /* correctly parsed, but illegal */
1195                 goto FINISHED;
1196
1197         case LDAP_SCOPE_BASE:
1198         case LDAP_SCOPE_ONELEVEL:
1199         case LDAP_SCOPE_SUBTREE:
1200 #ifdef LDAP_SCOPE_SUBORDINATE
1201         case LDAP_SCOPE_SUBORDINATE:
1202 #endif
1203                 /* do a search */
1204                 break;
1205
1206         default:
1207                 /* catch unhandled cases (there shouldn't be) */
1208                 assert( 0 );
1209         }
1210
1211 #ifdef NEW_LOGGING
1212         LDAP_LOG( TRANSPORT, DETAIL1, 
1213                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1214                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1215 #else
1216         Debug( LDAP_DEBUG_TRACE,
1217                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1218                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1219 #endif
1220
1221         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1222                 goto FINISHED;
1223         }
1224
1225         op.o_conn = opx->o_conn;
1226         op.o_connid = opx->o_connid;
1227         op.o_tag = LDAP_REQ_SEARCH;
1228         op.o_protocol = LDAP_VERSION3;
1229         op.o_ndn = opx->o_conn->c_ndn;
1230         op.o_callback = &cb;
1231         op.o_time = slap_get_time();
1232         op.o_do_not_cache = 1;
1233         op.o_is_auth_check = 1;
1234         op.o_threadctx = opx->o_threadctx;
1235         op.o_tmpmemctx = opx->o_tmpmemctx;
1236         op.o_tmpmfuncs = opx->o_tmpmfuncs;
1237 #ifdef LDAP_SLAPI
1238         op.o_pb = opx->o_pb;
1239 #endif
1240         op.oq_search.rs_deref = LDAP_DEREF_NEVER;
1241         op.oq_search.rs_slimit = 1;
1242         op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
1243         op.oq_search.rs_attrsonly = 1;
1244         /* use req_ndn as req_dn instead of non-pretty base of uri */
1245         if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
1246         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1247
1248         op.o_bd->be_search( &op, &rs );
1249         
1250 FINISHED:
1251         if( !BER_BVISEMPTY( sasldn ) ) {
1252                 opx->o_conn->c_authz_backend = op.o_bd;
1253         }
1254         if( !BER_BVISNULL( &op.o_req_dn ) ) {
1255                 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1256         }
1257         if( !BER_BVISNULL( &op.o_req_ndn ) ) {
1258                 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1259         }
1260         if( op.oq_search.rs_filter ) {
1261                 filter_free_x( opx, op.oq_search.rs_filter );
1262         }
1263         if( !BER_BVISNULL( &op.ors_filterstr ) ) {
1264                 ch_free( op.ors_filterstr.bv_val );
1265         }
1266
1267 #ifdef NEW_LOGGING
1268         LDAP_LOG( TRANSPORT, ENTRY, 
1269                 "slap_sasl2dn: Converted SASL name to %s\n",
1270                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
1271 #else
1272         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
1273                 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
1274 #endif
1275
1276         return;
1277 }
1278
1279
1280 /* Check if a bind can SASL authorize to another identity.
1281  * The DNs should not have the dn: prefix
1282  */
1283
1284 int slap_sasl_authorized( Operation *op,
1285         struct berval *authcDN, struct berval *authzDN )
1286 {
1287         int rc = LDAP_INAPPROPRIATE_AUTH;
1288
1289         /* User binding as anonymous */
1290         if ( authzDN == NULL ) {
1291                 rc = LDAP_SUCCESS;
1292                 goto DONE;
1293         }
1294
1295 #ifdef NEW_LOGGING
1296         LDAP_LOG( TRANSPORT, ENTRY, 
1297                 "slap_sasl_authorized: can %s become %s?\n", 
1298                 authcDN->bv_val, authzDN->bv_val, 0 );
1299 #else
1300         Debug( LDAP_DEBUG_TRACE,
1301            "==>slap_sasl_authorized: can %s become %s?\n",
1302                 authcDN->bv_val, authzDN->bv_val, 0 );
1303 #endif
1304
1305         /* If person is authorizing to self, succeed */
1306         if ( dn_match( authcDN, authzDN ) ) {
1307                 rc = LDAP_SUCCESS;
1308                 goto DONE;
1309         }
1310
1311         /* Allow the manager to authorize as any DN. */
1312         if( op->o_conn->c_authz_backend &&
1313                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1314         {
1315                 rc = LDAP_SUCCESS;
1316                 goto DONE;
1317         }
1318
1319         /* Check source rules */
1320         if( authz_policy & SASL_AUTHZ_TO ) {
1321                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1322                         slap_schema.si_ad_saslAuthzTo, authcDN );
1323                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1324                         goto DONE;
1325                 }
1326         }
1327
1328         /* Check destination rules */
1329         if( authz_policy & SASL_AUTHZ_FROM ) {
1330                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1331                         slap_schema.si_ad_saslAuthzFrom, authcDN );
1332                 if( rc == LDAP_SUCCESS ) {
1333                         goto DONE;
1334                 }
1335         }
1336
1337         rc = LDAP_INAPPROPRIATE_AUTH;
1338
1339 DONE:
1340
1341 #ifdef NEW_LOGGING
1342         LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1343 #else
1344         Debug( LDAP_DEBUG_TRACE,
1345                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
1346 #endif
1347
1348         return( rc );
1349 }