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(®, op.o_req_ndn.bv_val,
1840 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1841 if ( rc == 0 ) {
1842 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1843 regfree( ® );
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, ®out, 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, ®out, &base, &op.o_req_ndn,
2034 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
2035 if ( !BER_BVISNULL( ®out ) ) 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