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