]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
Set peeraddr also for IPv6, fixes ITS#1918
[openldap] / servers / slapd / saslauthz.c
1 /*
2  * Copyright (c) 2000, Mark Adamson, Carnegie Mellon.  All rights reserved.
3  * This software is not subject to any license of Carnegie Mellon University.
4  *
5  * Redistribution and use in source and binary forms are permitted without 
6  * restriction or fee of any kind as long as this notice is preserved.
7  *
8  * The name "Carnegie Mellon" must not be used to endorse or promote
9  * products derived from this software without prior written permission.
10  *
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/stdlib.h>
18 #include <ac/string.h>
19
20 #include "slap.h"
21
22 #ifdef HAVE_CYRUS_SASL
23 #include <limits.h>
24
25 #ifdef HAVE_SASL_SASL_H
26 #include <sasl/sasl.h>
27 #else
28 #include <sasl.h>
29 #endif
30
31 #include <ldap_pvt.h>
32
33 #define SASLREGEX_REPLACE 10
34
35 typedef struct sasl_uri {
36   struct berval dn;
37   struct berval filter;
38   int scope;
39 } SaslUri_t;
40
41 typedef struct sasl_regexp {
42   char *sr_match;                                                       /* regexp match pattern */
43   SaslUri_t sr_replace;                                                 /* regexp replace pattern */
44   regex_t sr_workspace;                                         /* workspace for regexp engine */
45   regmatch_t sr_strings[SASLREGEX_REPLACE];     /* strings matching $1,$2 ... */
46   int sr_dn_offset[SASLREGEX_REPLACE+2];                /* offsets of $1,$2... in *replace */
47   int sr_fi_offset[SASLREGEX_REPLACE+2];                /* offsets of $1,$2... in *replace */
48 } SaslRegexp_t;
49
50 static int nSaslRegexp = 0;
51 static SaslRegexp_t *SaslRegexp = NULL;
52
53 /* What SASL proxy authorization policies are allowed? */
54 #define SASL_AUTHZ_NONE 0
55 #define SASL_AUTHZ_FROM 1
56 #define SASL_AUTHZ_TO   2
57
58 static int authz_policy = SASL_AUTHZ_NONE;
59
60 int slap_sasl_setpolicy( const char *arg )
61 {
62         int rc = LDAP_SUCCESS;
63
64         if ( strcasecmp( arg, "none" ) == 0 )
65                 authz_policy = SASL_AUTHZ_NONE;
66         else if ( strcasecmp( arg, "from" ) == 0 )
67                 authz_policy = SASL_AUTHZ_FROM;
68         else if ( strcasecmp( arg, "to" ) == 0 )
69                 authz_policy = SASL_AUTHZ_TO;
70         else if ( strcasecmp( arg, "both" ) == 0 )
71                 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
72         else
73                 rc = LDAP_OTHER;
74         return rc;
75 }
76
77 /* URI format: ldap://<host>/<base>[?[<attrs>][?[<scope>][?[<filter>]]]] */
78
79 static int slap_parseURI( struct berval *uri,
80         struct berval *searchbase, int *scope, Filter **filter,
81         struct berval *fstr )
82 {
83         struct berval bv;
84         int rc;
85         LDAPURLDesc *ludp;
86
87         assert( uri != NULL && uri->bv_val != NULL );
88         searchbase->bv_val = NULL;
89         searchbase->bv_len = 0;
90         *scope = -1;
91         *filter = NULL;
92
93         if ( fstr ) {
94                 fstr->bv_val = NULL;
95                 fstr->bv_len = 0;
96         }
97
98 #ifdef NEW_LOGGING
99         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
100                 "slap_parseURI: parsing %s\n", uri->bv_val ));
101 #else
102         Debug( LDAP_DEBUG_TRACE, "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
103 #endif
104
105         /* If it does not look like a URI, assume it is a DN */
106         if( !strncasecmp( uri->bv_val, "dn:", sizeof("dn:")-1 ) ) {
107                 bv.bv_val = uri->bv_val + sizeof("dn:")-1;
108                 bv.bv_val += strspn( bv.bv_val, " " );
109
110 is_dn:          bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
111                 rc = dnNormalize2( NULL, &bv, searchbase );
112                 if (rc == LDAP_SUCCESS) {
113                         *scope = LDAP_SCOPE_BASE;
114                 }
115                 return( rc );
116         }
117
118         rc = ldap_url_parse( uri->bv_val, &ludp );
119         if ( rc == LDAP_URL_ERR_BADSCHEME ) {
120                 bv.bv_val = uri->bv_val;
121                 goto is_dn;
122         }
123
124         if ( rc != LDAP_URL_SUCCESS ) {
125                 return( LDAP_PROTOCOL_ERROR );
126         }
127
128         if ( ludp->lud_host && *ludp->lud_host ) {
129                 /* host part should be empty */
130                 return( LDAP_PROTOCOL_ERROR );
131         }
132
133         /* Grab the scope */
134         *scope = ludp->lud_scope;
135
136         /* Grab the filter */
137         if ( ludp->lud_filter ) {
138                 *filter = str2filter( ludp->lud_filter );
139                 if ( *filter == NULL )
140                         rc = LDAP_PROTOCOL_ERROR;
141                 else if ( fstr )
142                         ber_str2bv( ludp->lud_filter, 0, 1, fstr );
143         }
144
145         /* Grab the searchbase */
146         if ( rc == LDAP_URL_SUCCESS ) {
147                 bv.bv_val = ludp->lud_dn;
148                 bv.bv_len = strlen( bv.bv_val );
149                 rc = dnNormalize2( NULL, &bv, searchbase );
150         }
151
152         ldap_free_urldesc( ludp );
153
154         return( rc );
155 }
156
157 static int slap_sasl_rx_off(char *rep, int *off)
158 {
159         const char *c;
160         int n;
161
162         /* Precompile replace pattern. Find the $<n> placeholders */
163         off[0] = -2;
164         n = 1;
165         for ( c = rep;   *c;  c++ ) {
166                 if ( *c == '\\' && c[1] ) {
167                         c++;
168                         continue;
169                 }
170                 if ( *c == '$' ) {
171                         if ( n == SASLREGEX_REPLACE ) {
172 #ifdef NEW_LOGGING
173                                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
174                                         "slap_sasl_regexp_config: \"%s\" has too many $n "
175                                                 "placeholders (max %d)\n",
176                                         rep, SASLREGEX_REPLACE ));
177 #else
178                                 Debug( LDAP_DEBUG_ANY,
179                                         "SASL replace pattern %s has too many $n "
180                                                 "placeholders (max %d)\n",
181                                         rep, SASLREGEX_REPLACE, 0 );
182 #endif
183
184                                 return( LDAP_OPERATIONS_ERROR );
185                         }
186                         off[n] = c - rep;
187                         n++;
188                 }
189         }
190
191         /* Final placeholder, after the last $n */
192         off[n] = c - rep;
193         n++;
194         off[n] = -1;
195         return( LDAP_SUCCESS );
196 }
197 #endif /* HAVE_CYRUS_SASL */
198
199 int slap_sasl_regexp_config( const char *match, const char *replace )
200 {
201 #ifdef HAVE_CYRUS_SASL
202         const char *c;
203         int rc, n;
204         SaslRegexp_t *reg;
205         struct berval bv, nbv;
206         Filter *filter;
207
208         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
209           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
210         reg = &( SaslRegexp[nSaslRegexp] );
211         ber_str2bv( match, 0, 0, &bv );
212         rc = dnNormalize2( NULL, &bv, &nbv );
213         if ( rc ) {
214 #ifdef NEW_LOGGING
215                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
216                            "slap_sasl_regexp_config: \"%s\" could not be normalized.\n",
217                            match ));
218 #else
219                 Debug( LDAP_DEBUG_ANY,
220                 "SASL match pattern %s could not be normalized.\n",
221                 match, 0, 0 );
222 #endif
223                 return( rc );
224         }
225         reg->sr_match = nbv.bv_val;
226
227         ber_str2bv( replace, 0, 0, &bv );
228         rc = slap_parseURI( &bv, &reg->sr_replace.dn, &reg->sr_replace.scope,
229                 &filter, &reg->sr_replace.filter );
230         if ( filter ) filter_free( filter );
231         if ( rc ) {
232 #ifdef NEW_LOGGING
233                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
234                            "slap_sasl_regexp_config: \"%s\" could not be parsed.\n",
235                            replace ));
236 #else
237                 Debug( LDAP_DEBUG_ANY,
238                 "SASL replace pattern %s could not be parsed.\n",
239                 replace, 0, 0 );
240 #endif
241                 return( rc );
242         }
243
244         /* Precompile matching pattern */
245         rc = regcomp( &reg->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
246         if ( rc ) {
247 #ifdef NEW_LOGGING
248                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
249                            "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
250                            reg->sr_match ));
251 #else
252                 Debug( LDAP_DEBUG_ANY,
253                 "SASL match pattern %s could not be compiled by regexp engine\n",
254                 reg->sr_match, 0, 0 );
255 #endif
256
257                 return( LDAP_OPERATIONS_ERROR );
258         }
259
260         rc = slap_sasl_rx_off( reg->sr_replace.dn.bv_val, reg->sr_dn_offset );
261         if ( rc != LDAP_SUCCESS ) return rc;
262
263         if (reg->sr_replace.filter.bv_val ) {
264                 rc = slap_sasl_rx_off( reg->sr_replace.filter.bv_val, reg->sr_fi_offset );
265                 if ( rc != LDAP_SUCCESS ) return rc;
266         }
267
268         nSaslRegexp++;
269 #endif
270         return( LDAP_SUCCESS );
271 }
272
273
274 #ifdef HAVE_CYRUS_SASL
275
276 /* Perform replacement on regexp matches */
277 static void slap_sasl_rx_exp( char *rep, int *off, regmatch_t *str,
278         char *saslname, struct berval *out )
279 {
280         int i, n, len, insert;
281
282         /* Get the total length of the final URI */
283
284         n=1;
285         len = 0;
286         while( off[n] >= 0 ) {
287                 /* Len of next section from replacement string (x,y,z above) */
288                 len += off[n] - off[n-1] - 2;
289                 if( off[n+1] < 0)
290                         break;
291
292                 /* Len of string from saslname that matched next $i  (b,d above) */
293                 i = rep[ off[n] + 1 ]   - '0';
294                 len += str[i].rm_eo - str[i].rm_so;
295                 n++;
296         }
297         out->bv_val = ch_malloc( len + 1 );
298         out->bv_len = len;
299
300         /* Fill in URI with replace string, replacing $i as we go */
301         n=1;
302         insert = 0;
303         while( off[n] >= 0) {
304                 /* Paste in next section from replacement string (x,y,z above) */
305                 len = off[n] - off[n-1] - 2;
306                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
307                 insert += len;
308                 if( off[n+1] < 0)
309                         break;
310
311                 /* Paste in string from saslname that matched next $i  (b,d above) */
312                 i = rep[ off[n] + 1 ]   - '0';
313                 len = str[i].rm_eo - str[i].rm_so;
314                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
315                 insert += len;
316
317                 n++;
318         }
319
320         out->bv_val[insert] = '\0';
321 }
322
323 /* Take the passed in SASL name and attempt to convert it into an
324    LDAP URI to find the matching LDAP entry, using the pattern matching
325    strings given in the saslregexp config file directive(s) */
326
327 static int slap_sasl_regexp( struct berval *in, SaslUri_t *out )
328 {
329         char *saslname = in->bv_val;
330         char *scope[] = { "base", "one", "sub" };
331         SaslRegexp_t *reg;
332         int i;
333
334         memset( out, 0, sizeof( *out ) );
335
336 #ifdef NEW_LOGGING
337         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
338                 "slap_sasl_regexp: converting SASL name %s\n", saslname ));
339 #else
340         Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
341            saslname, 0, 0 );
342 #endif
343
344         if (( saslname == NULL ) || ( nSaslRegexp == 0 ))
345                 return( 0 );
346
347         /* Match the normalized SASL name to the saslregexp patterns */
348         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
349                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
350                   reg->sr_strings, 0)  == 0 )
351                         break;
352         }
353
354         if( i >= nSaslRegexp )
355                 return( 0 );
356
357         /*
358          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
359          * replace pattern of the form "x$1y$2z". The returned string needs
360          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
361          */
362         slap_sasl_rx_exp( reg->sr_replace.dn.bv_val, reg->sr_dn_offset,
363                 reg->sr_strings, saslname, &out->dn );
364
365         if ( reg->sr_replace.filter.bv_val )
366                 slap_sasl_rx_exp( reg->sr_replace.filter.bv_val,
367                         reg->sr_fi_offset, reg->sr_strings, saslname, &out->filter );
368         
369         out->scope = reg->sr_replace.scope;
370
371 #ifdef NEW_LOGGING
372         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
373                 "slap_sasl_regexp: converted SASL name to ldap:///%s??%s?%s\n",
374                 out->dn.bv_val, scope[out->scope], out->filter.bv_val ?
375                 out->filter.bv_val : "" ));
376 #else
377         Debug( LDAP_DEBUG_TRACE,
378            "slap_sasl_regexp: converted SASL name to ldap:///%s??%s?%s\n",
379                 out->dn.bv_val, scope[out->scope], out->filter.bv_val ?
380                 out->filter.bv_val : "" );
381 #endif
382
383         return( 1 );
384 }
385
386 /* Two empty callback functions to avoid sending results */
387 static void sasl_sc_r( Connection *conn, Operation *o, ber_tag_t tag,
388         ber_int_t msgid, ber_int_t err, const char *matched,
389         const char *text, BerVarray ref, const char *resoid,
390         struct berval *resdata, struct berval *sasldata, LDAPControl **c)
391 {
392 }
393
394 static void sasl_sc_s( Connection *conn, Operation *o, ber_int_t err,
395         const char *matched, const char *text, BerVarray refs, LDAPControl **c,
396         int nentries)
397 {
398 }
399
400 /* This callback actually does some work...*/
401 static int sasl_sc_sasl2dn( BackendDB *be, Connection *conn, Operation *o,
402         Entry *e, AttributeName *an, int ao, LDAPControl **c)
403 {
404         struct berval *ndn = o->o_callback->sc_private;
405
406         /* We only want to be called once */
407         if( ndn->bv_val ) {
408                 free(ndn->bv_val);
409                 ndn->bv_val = NULL;
410
411 #ifdef NEW_LOGGING
412                 LDAP_LOG(( "sasl", LDAP_LEVEL_DETAIL1,
413                         "slap_sasl2dn: search DN returned more than 1 entry\n" ));
414 #else
415                 Debug( LDAP_DEBUG_TRACE,
416                         "slap_sasl2dn: search DN returned more than 1 entry\n", 0,0,0 );
417 #endif
418                 return -1;
419         }
420
421         ber_dupbv(ndn, &e->e_nname);
422         return 0;
423 }
424
425 /*
426  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
427  * return the LDAP DN to which it matches. The SASL regexp rules in the config
428  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
429  * search with scope=base), just return the URI (or its searchbase). Otherwise
430  * an internal search must be done, and if that search returns exactly one
431  * entry, return the DN of that one entry.
432  */
433
434 void slap_sasl2dn( Connection *conn, struct berval *saslname, struct berval *dn )
435 {
436         int rc;
437         Backend *be;
438         Filter *filter=NULL;
439         slap_callback cb = {sasl_sc_r, sasl_sc_s, sasl_sc_sasl2dn, NULL};
440         Operation op = {0};
441         SaslUri_t uri;
442
443 #ifdef NEW_LOGGING
444         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
445                 "slap_sasl2dn: converting SASL name %s to DN.\n", saslname->bv_val ));
446 #else
447         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
448                 "converting SASL name %s to a DN\n", saslname->bv_val, 0,0 );
449 #endif
450
451         dn->bv_val = NULL;
452         dn->bv_len = 0;
453         cb.sc_private = dn;
454
455         /* Convert the SASL name into a minimal URI */
456         if( !slap_sasl_regexp( saslname, &uri ) )
457                 goto FINISHED;
458
459         if ( uri.filter.bv_val )
460                 filter = str2filter( uri.filter.bv_val );
461
462         /* Must do an internal search */
463
464         be = select_backend( &uri.dn, 0, 1 );
465
466         /* Massive shortcut: search scope == base */
467         if( uri.scope == LDAP_SCOPE_BASE ) {
468                 *dn = uri.dn;
469                 uri.dn.bv_len = 0;
470                 uri.dn.bv_val = NULL;
471                 goto FINISHED;
472         }
473
474 #ifdef NEW_LOGGING
475         LDAP_LOG(( "sasl", LDAP_LEVEL_DETAIL1,
476                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
477                 uri.dn.bv_val, uri.scope ));
478 #else
479         Debug( LDAP_DEBUG_TRACE,
480            "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
481            uri.dn.bv_val, uri.scope, 0 );
482 #endif
483
484         if(( be == NULL ) || ( be->be_search == NULL)) {
485                 goto FINISHED;
486         }
487         suffix_alias( be, &uri.dn );
488
489         op.o_tag = LDAP_REQ_SEARCH;
490         op.o_protocol = LDAP_VERSION3;
491         op.o_ndn = *saslname;
492         op.o_callback = &cb;
493         op.o_time = slap_get_time();
494
495         (*be->be_search)( be, NULL, &op, NULL, &uri.dn,
496                 uri.scope, LDAP_DEREF_NEVER, 1, 0,
497                 filter, NULL, NULL, 1 );
498         
499         if( dn->bv_len ) {
500                 conn->c_authz_backend = be;
501         }
502
503 FINISHED:
504         if( uri.dn.bv_len ) ch_free( uri.dn.bv_val );
505         if( uri.filter.bv_len ) ch_free( uri.filter.bv_val );
506         if( filter ) filter_free( filter );
507
508 #ifdef NEW_LOGGING
509         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
510                 "slap_sasl2dn: Converted SASL name to %s\n",
511                 dn->bv_len ? dn->bv_val : "<nothing>" ));
512 #else
513         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
514                 dn->bv_len ? dn->bv_val : "<nothing>", 0, 0 );
515 #endif
516
517         return;
518 }
519
520 typedef struct smatch_info {
521         struct berval *dn;
522         int match;
523 } smatch_info;
524
525 static int sasl_sc_smatch( BackendDB *be, Connection *conn, Operation *o,
526         Entry *e, AttributeName *an, int ao, LDAPControl **c)
527 {
528         smatch_info *sm = o->o_callback->sc_private;
529
530         if (dn_match(sm->dn, &e->e_nname)) {
531                 sm->match = 1;
532                 return -1;      /* short-circuit the search */
533         } else {
534                 return 1;
535         }
536 }
537
538 /*
539  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
540  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
541  * the rule must be used as an internal search for entries. If that search
542  * returns the *assertDN entry, the match is successful.
543  *
544  * The assertDN should not have the dn: prefix
545  */
546
547 static
548 int slap_sasl_match( struct berval *rule, struct berval *assertDN, struct berval *authc )
549 {
550         struct berval searchbase = {0, NULL};
551         int rc, scope;
552         Backend *be;
553         Filter *filter=NULL;
554         regex_t reg;
555         smatch_info sm;
556         slap_callback cb = { sasl_sc_r, sasl_sc_s, sasl_sc_smatch, NULL };
557         Operation op = {0};
558
559 #ifdef NEW_LOGGING
560         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
561                 "slap_sasl_match: comparing DN %s to rule %s\n", assertDN->bv_val, rule->bv_val ));
562 #else
563         Debug( LDAP_DEBUG_TRACE,
564            "===>slap_sasl_match: comparing DN %s to rule %s\n", assertDN->bv_val, rule->bv_val, 0 );
565 #endif
566
567         rc = slap_parseURI( rule, &searchbase, &scope, &filter, NULL );
568         if( rc != LDAP_SUCCESS )
569                 goto CONCLUDED;
570
571         /* Massive shortcut: search scope == base */
572         if( scope == LDAP_SCOPE_BASE ) {
573                 rc = regcomp(&reg, searchbase.bv_val,
574                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
575                 if ( rc == 0 ) {
576                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
577                         regfree( &reg );
578                 }
579                 if ( rc == 0 )
580                         rc = LDAP_SUCCESS;
581                 else
582                         rc = LDAP_INAPPROPRIATE_AUTH;
583                 goto CONCLUDED;
584         }
585
586         /* Must run an internal search. */
587
588 #ifdef NEW_LOGGING
589         LDAP_LOG(( "sasl", LDAP_LEVEL_DETAIL1,
590                 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
591                 searchbase.bv_val, scope ));
592 #else
593         Debug( LDAP_DEBUG_TRACE,
594            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
595            searchbase.bv_val, scope, 0 );
596 #endif
597
598         be = select_backend( &searchbase, 0, 1 );
599         if(( be == NULL ) || ( be->be_search == NULL)) {
600                 rc = LDAP_INAPPROPRIATE_AUTH;
601                 goto CONCLUDED;
602         }
603         suffix_alias( be, &searchbase );
604
605         sm.dn = assertDN;
606         sm.match = 0;
607         cb.sc_private = &sm;
608
609         op.o_tag = LDAP_REQ_SEARCH;
610         op.o_protocol = LDAP_VERSION3;
611         op.o_ndn = *authc;
612         op.o_callback = &cb;
613         op.o_time = slap_get_time();
614
615         (*be->be_search)( be, /*conn=*/NULL, &op, /*base=*/NULL, &searchbase,
616            scope, /*deref=*/1, /*sizelimit=*/0, /*time=*/0, filter, /*fstr=*/NULL,
617            /*attrs=*/NULL, /*attrsonly=*/0 );
618
619         if (sm.match)
620                 rc = LDAP_SUCCESS;
621         else
622                 rc = LDAP_INAPPROPRIATE_AUTH;
623
624 CONCLUDED:
625         if( searchbase.bv_len ) ch_free( searchbase.bv_val );
626         if( filter ) filter_free( filter );
627 #ifdef NEW_LOGGING
628         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
629                    "slap_sasl_match: comparison returned %d\n", rc ));
630 #else
631         Debug( LDAP_DEBUG_TRACE,
632            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
633 #endif
634
635         return( rc );
636 }
637
638
639 /*
640  * This function answers the question, "Can this ID authorize to that ID?",
641  * based on authorization rules. The rules are stored in the *searchDN, in the
642  * attribute named by *attr. If any of those rules map to the *assertDN, the
643  * authorization is approved.
644  *
645  * The DNs should not have the dn: prefix
646  */
647 static int
648 slap_sasl_check_authz(struct berval *searchDN, struct berval *assertDN, AttributeDescription *ad, struct berval *authc)
649 {
650         int i, rc;
651         BerVarray vals=NULL;
652
653 #ifdef NEW_LOGGING
654         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
655                    "slap_sasl_check_authz: does %s match %s rule in %s?\n",
656                    assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val ));
657 #else
658         Debug( LDAP_DEBUG_TRACE,
659            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
660            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
661 #endif
662
663         rc = backend_attribute( NULL, NULL, NULL, NULL, searchDN, ad, &vals );
664         if( rc != LDAP_SUCCESS )
665                 goto COMPLETE;
666
667         /* Check if the *assertDN matches any **vals */
668         for( i=0; vals[i].bv_val != NULL; i++ ) {
669                 rc = slap_sasl_match( &vals[i], assertDN, authc );
670                 if ( rc == LDAP_SUCCESS )
671                         goto COMPLETE;
672         }
673         rc = LDAP_INAPPROPRIATE_AUTH;
674
675 COMPLETE:
676         if( vals ) ber_bvarray_free( vals );
677
678 #ifdef NEW_LOGGING
679         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
680                    "slap_sasl_check_authz: %s check returning %s\n", ad->ad_cname.bv_val, rc ));
681 #else
682         Debug( LDAP_DEBUG_TRACE,
683            "<==slap_sasl_check_authz: %s check returning %d\n", ad->ad_cname.bv_val, rc, 0);
684 #endif
685
686         return( rc );
687 }
688 #endif  /* HAVE_CYRUS_SASL */
689
690
691 /* Check if a bind can SASL authorize to another identity.
692  * The DNs should not have the dn: prefix
693  */
694
695 int slap_sasl_authorized( struct berval *authcDN, struct berval *authzDN )
696 {
697         int rc = LDAP_INAPPROPRIATE_AUTH;
698
699 #ifdef HAVE_CYRUS_SASL
700         /* User binding as anonymous */
701         if ( authzDN == NULL ) {
702                 rc = LDAP_SUCCESS;
703                 goto DONE;
704         }
705
706 #ifdef NEW_LOGGING
707         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
708                 "slap_sasl_authorized: can %s become %s?\n", authcDN->bv_val, authzDN->bv_val ));
709 #else
710         Debug( LDAP_DEBUG_TRACE,
711            "==>slap_sasl_authorized: can %s become %s?\n", authcDN->bv_val, authzDN->bv_val, 0 );
712 #endif
713
714         /* If person is authorizing to self, succeed */
715         if ( dn_match( authcDN, authzDN ) ) {
716                 rc = LDAP_SUCCESS;
717                 goto DONE;
718         }
719
720         /* Check source rules */
721         if( authz_policy & SASL_AUTHZ_TO ) {
722                 rc = slap_sasl_check_authz( authcDN, authzDN,
723                         slap_schema.si_ad_saslAuthzTo, authcDN );
724                 if( rc == LDAP_SUCCESS ) {
725                         goto DONE;
726                 }
727         }
728
729         /* Check destination rules */
730         if( authz_policy & SASL_AUTHZ_FROM ) {
731                 rc = slap_sasl_check_authz( authzDN, authcDN,
732                         slap_schema.si_ad_saslAuthzFrom, authcDN );
733                 if( rc == LDAP_SUCCESS ) {
734                         goto DONE;
735                 }
736         }
737
738         rc = LDAP_INAPPROPRIATE_AUTH;
739
740 DONE:
741 #endif
742
743 #ifdef NEW_LOGGING
744         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
745                 "slap_sasl_authorized: return %d\n", rc ));
746 #else
747         Debug( LDAP_DEBUG_TRACE,
748                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
749 #endif
750
751         return( rc );
752 }