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