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