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/>.
4
* Copyright 1998-2008 The OpenLDAP Foundation.
5
* Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted only as authorized by the OpenLDAP
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>.
24
#include <ac/stdlib.h>
25
#include <ac/string.h>
32
#define SASLREGEX_REPLACE 10
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)
43
* IDs in DNauthzid form can now have a type specifier, that
44
* influences how they are used in related operations.
46
* syntax: dn[.{exact|regex}]:<val>
48
* dn.exact: the value must pass normalization and is used
50
* dn.regex: the value is treated as a regular expression
51
* in matching DN values in authz{To|From}
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
59
* IDs in DNauthzid form can now have a type specifier, that
60
* influences how they are used in related operations.
62
* syntax: u[.mech[/realm]]:<val>
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).
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 */
76
static int nSaslRegexp = 0;
77
static SaslRegexp_t *SaslRegexp = NULL;
79
#ifdef SLAP_AUTH_REWRITE
81
struct rewrite_info *sasl_rwinfo = NULL;
82
#define AUTHID_CONTEXT "authid"
83
#endif /* SLAP_AUTH_REWRITE */
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
91
static const char *policy_txt[] = {
92
"none", "from", "to", "any"
95
static int authz_policy = SASL_AUTHZ_NONE;
98
slap_sasl_match( Operation *opx, struct berval *rule,
99
struct berval *assertDN, struct berval *authc );
101
int slap_sasl_setpolicy( const char *arg )
103
int rc = LDAP_SUCCESS;
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;
121
const char * slap_sasl_getpolicy()
123
if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
126
return policy_txt[authz_policy];
129
int slap_parse_user( struct berval *id, struct berval *user,
130
struct berval *realm, struct berval *mech )
134
assert( id != NULL );
135
assert( !BER_BVISNULL( id ) );
136
assert( user != NULL );
137
assert( realm != NULL );
138
assert( mech != NULL );
142
if ( u != 'u' && u != 'U' ) {
143
/* called with something other than u: */
144
return LDAP_PROTOCOL_ERROR;
148
* u[.mech[/realm]]:user
151
user->bv_val = ber_bvchr( id, ':' );
152
if ( BER_BVISNULL( user ) ) {
153
return LDAP_PROTOCOL_ERROR;
155
user->bv_val[ 0 ] = '\0';
157
user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
159
mech->bv_val = ber_bvchr( id, '.' );
160
if ( !BER_BVISNULL( mech ) ) {
161
mech->bv_val[ 0 ] = '\0';
163
mech->bv_len = user->bv_val - mech->bv_val - 1;
165
realm->bv_val = ber_bvchr( mech, '/' );
167
if ( !BER_BVISNULL( realm ) ) {
168
realm->bv_val[ 0 ] = '\0';
170
mech->bv_len = realm->bv_val - mech->bv_val - 1;
171
realm->bv_len = user->bv_val - realm->bv_val - 1;
178
if ( id->bv_val[ 1 ] != '\0' ) {
179
return LDAP_PROTOCOL_ERROR;
182
if ( !BER_BVISNULL( mech ) ) {
183
assert( mech->bv_val == id->bv_val + 2 );
185
AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
189
if ( !BER_BVISNULL( realm ) ) {
190
assert( realm->bv_val >= id->bv_val + 2 );
192
AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
196
/* leave "u:" before user */
199
user->bv_val[ 0 ] = u;
200
user->bv_val[ 1 ] = ':';
211
int rc = LDAP_INVALID_SYNTAX;
212
LDAPURLDesc *ludp = NULL;
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>
224
assert( in != NULL );
225
assert( !BER_BVISNULL( in ) );
227
Debug( LDAP_DEBUG_TRACE,
228
"authzValidate: parsing %s\n", in->bv_val, 0, 0 );
231
* 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
232
* 3) dn.regex:<pattern>
234
* <DN> must pass DN normalization
236
if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
237
bv.bv_val = in->bv_val + STRLENOF( "dn" );
239
if ( bv.bv_val[ 0 ] == '.' ) {
242
if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
243
bv.bv_val += STRLENOF( "exact:" );
244
scope = LDAP_X_SCOPE_EXACT;
246
} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
247
bv.bv_val += STRLENOF( "regex:" );
248
scope = LDAP_X_SCOPE_REGEX;
250
} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
251
bv.bv_val += STRLENOF( "children:" );
252
scope = LDAP_X_SCOPE_CHILDREN;
254
} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
255
bv.bv_val += STRLENOF( "subtree:" );
256
scope = LDAP_X_SCOPE_SUBTREE;
258
} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
259
bv.bv_val += STRLENOF( "onelevel:" );
260
scope = LDAP_X_SCOPE_ONELEVEL;
263
return LDAP_INVALID_SYNTAX;
267
if ( bv.bv_val[ 0 ] != ':' ) {
268
return LDAP_INVALID_SYNTAX;
270
scope = LDAP_X_SCOPE_EXACT;
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 );
279
/* a single '*' means any DN without using regexes */
280
if ( ber_bvccmp( &bv, '*' ) ) {
281
/* LDAP_X_SCOPE_USERS */
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 );
292
case LDAP_X_SCOPE_REGEX:
299
* 4) u[.mech[/realm]]:<ID>
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 ] == '.' ) )
306
char buf[ SLAP_LDAPDN_MAXLEN ];
312
if ( sizeof( buf ) <= in->bv_len ) {
313
return LDAP_INVALID_SYNTAX;
316
id.bv_len = in->bv_len;
318
strncpy( buf, in->bv_val, sizeof( buf ) );
320
rc = slap_parse_user( &id, &user, &realm, &mech );
321
if ( rc != LDAP_SUCCESS ) {
322
return LDAP_INVALID_SYNTAX;
328
* 5) group[/groupClass[/memberAttr]]:<DN>
330
* <groupClass> defaults to "groupOfNames"
331
* <memberAttr> defaults to "member"
333
* <DN> must pass DN normalization
335
} else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
337
struct berval group_dn = BER_BVNULL,
338
group_oc = BER_BVNULL,
339
member_at = BER_BVNULL;
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;
352
* FIXME: we assume that "member" and "groupOfNames"
353
* are present in schema...
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;
359
member_at.bv_val = ber_bvchr( &group_oc, '/' );
360
if ( member_at.bv_val ) {
361
AttributeDescription *ad = NULL;
362
const char *text = NULL;
364
group_oc.bv_len = member_at.bv_val - group_oc.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 ) {
373
if ( oc_bvfind( &group_oc ) == NULL ) {
374
return LDAP_INVALID_SYNTAX;
379
group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
381
rc = dnValidate( NULL, &group_dn );
382
if ( rc != LDAP_SUCCESS ) {
390
* ldap:///<base>??<scope>?<filter>
391
* <scope> ::= {base|one|subtree}
393
* <scope> defaults to "base"
394
* <base> must pass DN normalization
395
* <filter> must pass str2filter()
397
rc = ldap_url_parse( in->bv_val, &ludp );
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
407
if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
411
rc = LDAP_INVALID_SYNTAX;
416
case LDAP_URL_ERR_BADSCHEME:
418
* last chance: assume it's a(n exact) DN ...
420
* NOTE: must pass DN normalization
422
ldap_free_urldesc( ludp );
423
bv.bv_val = in->bv_val;
424
scope = LDAP_X_SCOPE_EXACT;
428
rc = LDAP_INVALID_SYNTAX;
432
if ( ( ludp->lud_host && *ludp->lud_host )
433
|| ludp->lud_attrs || ludp->lud_exts )
435
/* host part must be empty */
436
/* attrs and extensions parts must be empty */
437
rc = LDAP_INVALID_SYNTAX;
441
/* Grab the filter */
442
if ( ludp->lud_filter ) {
443
Filter *f = str2filter( ludp->lud_filter );
445
rc = LDAP_INVALID_SYNTAX;
451
/* Grab the searchbase */
452
assert( ludp->lud_dn != NULL );
453
ber_str2bv( ludp->lud_dn, 0, 0, &bv );
454
rc = dnValidate( NULL, &bv );
457
ldap_free_urldesc( ludp );
464
struct berval *normalized,
469
int rc = LDAP_INVALID_SYNTAX;
470
LDAPURLDesc *ludp = NULL;
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>
484
assert( val != NULL );
485
assert( !BER_BVISNULL( val ) );
488
* 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
489
* 3) dn.regex:<pattern>
491
* <DN> must pass DN normalization
493
if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
494
struct berval out = BER_BVNULL,
498
bv.bv_val = val->bv_val + STRLENOF( "dn" );
500
if ( bv.bv_val[ 0 ] == '.' ) {
503
if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
504
bv.bv_val += STRLENOF( "exact:" );
505
scope = LDAP_X_SCOPE_EXACT;
507
} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
508
bv.bv_val += STRLENOF( "regex:" );
509
scope = LDAP_X_SCOPE_REGEX;
511
} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
512
bv.bv_val += STRLENOF( "children:" );
513
scope = LDAP_X_SCOPE_CHILDREN;
515
} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
516
bv.bv_val += STRLENOF( "subtree:" );
517
scope = LDAP_X_SCOPE_SUBTREE;
519
} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
520
bv.bv_val += STRLENOF( "onelevel:" );
521
scope = LDAP_X_SCOPE_ONELEVEL;
524
return LDAP_INVALID_SYNTAX;
528
if ( bv.bv_val[ 0 ] != ':' ) {
529
return LDAP_INVALID_SYNTAX;
531
scope = LDAP_X_SCOPE_EXACT;
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 );
540
/* a single '*' means any DN without using regexes */
541
if ( ber_bvccmp( &bv, '*' ) ) {
542
ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
547
case LDAP_X_SCOPE_EXACT:
548
case LDAP_X_SCOPE_CHILDREN:
549
case LDAP_X_SCOPE_SUBTREE:
550
case LDAP_X_SCOPE_ONELEVEL:
552
rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
554
rc = dnPretty( NULL, &bv, &out, ctx );
556
if( rc != LDAP_SUCCESS ) {
557
return LDAP_INVALID_SYNTAX;
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 );
570
return LDAP_INVALID_SYNTAX;
575
case LDAP_X_SCOPE_EXACT:
576
BER_BVSTR( &prefix, "dn:" );
579
case LDAP_X_SCOPE_CHILDREN:
580
BER_BVSTR( &prefix, "dn.children:" );
583
case LDAP_X_SCOPE_SUBTREE:
584
BER_BVSTR( &prefix, "dn.subtree:" );
587
case LDAP_X_SCOPE_ONELEVEL:
588
BER_BVSTR( &prefix, "dn.onelevel:" );
596
normalized->bv_len = prefix.bv_len + out.bv_len;
597
normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
599
ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
600
ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
602
ber_memfree_x( out.bv_val, ctx );
607
* 4) u[.mech[/realm]]:<ID>
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 ] == '.' ) )
614
char buf[ SLAP_LDAPDN_MAXLEN ];
620
if ( sizeof( buf ) <= val->bv_len ) {
621
return LDAP_INVALID_SYNTAX;
624
id.bv_len = val->bv_len;
626
strncpy( buf, val->bv_val, sizeof( buf ) );
628
rc = slap_parse_user( &id, &user, &realm, &mech );
629
if ( rc != LDAP_SUCCESS ) {
630
return LDAP_INVALID_SYNTAX;
633
ber_dupbv_x( normalized, val, ctx );
638
* 5) group[/groupClass[/memberAttr]]:<DN>
640
* <groupClass> defaults to "groupOfNames"
641
* <memberAttr> defaults to "member"
643
* <DN> must pass DN normalization
645
} else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
647
struct berval group_dn = BER_BVNULL,
648
group_oc = BER_BVNULL,
649
member_at = BER_BVNULL,
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;
664
* FIXME: we assume that "member" and "groupOfNames"
665
* are present in schema...
667
if ( bv.bv_val[ 0 ] == '/' ) {
668
ObjectClass *oc = NULL;
670
group_oc.bv_val = &bv.bv_val[ 1 ];
671
group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
673
member_at.bv_val = ber_bvchr( &group_oc, '/' );
674
if ( member_at.bv_val ) {
675
AttributeDescription *ad = NULL;
676
const char *text = NULL;
678
group_oc.bv_len = member_at.bv_val - group_oc.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 ) {
686
member_at = ad->ad_cname;
690
oc = oc_bvfind( &group_oc );
692
return LDAP_INVALID_SYNTAX;
695
group_oc = oc->soc_cname;
699
group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
702
rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
704
rc = dnPretty( NULL, &group_dn, &out, ctx );
706
if ( rc != LDAP_SUCCESS ) {
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;
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 ) ) {
723
ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
724
if ( !BER_BVISNULL( &member_at ) ) {
727
ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
732
ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
734
ber_memfree_x( out.bv_val, ctx );
740
* ldap:///<base>??<scope>?<filter>
741
* <scope> ::= {base|one|subtree}
743
* <scope> defaults to "base"
744
* <base> must pass DN normalization
745
* <filter> must pass str2filter()
747
rc = ldap_url_parse( val->bv_val, &ludp );
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
757
if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
761
rc = LDAP_INVALID_SYNTAX;
765
AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
768
case LDAP_URL_ERR_BADSCHEME:
770
* last chance: assume it's a(n exact) DN ...
772
* NOTE: must pass DN normalization
774
ldap_free_urldesc( ludp );
775
bv.bv_val = val->bv_val;
776
scope = LDAP_X_SCOPE_EXACT;
780
rc = LDAP_INVALID_SYNTAX;
784
if ( ( ludp->lud_host && *ludp->lud_host )
785
|| ludp->lud_attrs || ludp->lud_exts )
787
/* host part must be empty */
788
/* attrs and extensions parts must be empty */
789
rc = LDAP_INVALID_SYNTAX;
793
/* Grab the filter */
794
if ( ludp->lud_filter ) {
795
struct berval filterstr;
798
lud_filter = ludp->lud_filter;
800
f = str2filter( lud_filter );
802
rc = LDAP_INVALID_SYNTAX;
805
filter2bv( f, &filterstr );
807
if ( BER_BVISNULL( &filterstr ) ) {
808
rc = LDAP_INVALID_SYNTAX;
812
ludp->lud_filter = filterstr.bv_val;
815
/* Grab the searchbase */
816
assert( ludp->lud_dn != NULL );
817
if ( ludp->lud_dn ) {
818
struct berval out = BER_BVNULL;
820
lud_dn = ludp->lud_dn;
822
ber_str2bv( lud_dn, 0, 0, &bv );
824
rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
826
rc = dnPretty( NULL, &bv, &out, ctx );
829
if ( rc != LDAP_SUCCESS ) {
833
ludp->lud_dn = out.bv_val;
837
normalized->bv_val = ldap_url_desc2str( ludp );
838
if ( normalized->bv_val ) {
839
normalized->bv_len = strlen( normalized->bv_val );
842
rc = LDAP_INVALID_SYNTAX;
847
if ( ludp->lud_filter != lud_filter ) {
848
ber_memfree( ludp->lud_filter );
850
ludp->lud_filter = lud_filter;
854
if ( ludp->lud_dn != lud_dn ) {
855
ber_memfree( ludp->lud_dn );
857
ludp->lud_dn = lud_dn;
860
ldap_free_urldesc( ludp );
871
struct berval *normalized,
876
Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
879
rc = authzPrettyNormal( val, normalized, ctx, 1 );
881
Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
882
normalized->bv_val, rc, 0 );
896
Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
899
rc = authzPrettyNormal( val, out, ctx, 0 );
901
Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
902
out->bv_val, rc, 0 );
913
struct berval *nbase,
925
assert( uri != NULL && !BER_BVISNULL( uri ) );
932
Debug( LDAP_DEBUG_TRACE,
933
"slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
935
rc = LDAP_PROTOCOL_ERROR;
938
if ( idx.bv_val[ 0 ] == '{' ) {
941
ptr = ber_bvchr( &idx, '}' ) + 1;
943
assert( ptr != (void *)1 );
945
idx.bv_len -= ptr - idx.bv_val;
951
* dn[.<dnstyle>]:<dnpattern>
952
* <dnstyle> ::= {exact|regex|children|subtree|onelevel}
954
* <dnstyle> defaults to "exact"
955
* if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
957
if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
958
bv.bv_val = uri->bv_val + STRLENOF( "dn" );
960
if ( bv.bv_val[ 0 ] == '.' ) {
963
if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
964
bv.bv_val += STRLENOF( "exact:" );
965
*scope = LDAP_X_SCOPE_EXACT;
967
} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
968
bv.bv_val += STRLENOF( "regex:" );
969
*scope = LDAP_X_SCOPE_REGEX;
971
} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
972
bv.bv_val += STRLENOF( "children:" );
973
*scope = LDAP_X_SCOPE_CHILDREN;
975
} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
976
bv.bv_val += STRLENOF( "subtree:" );
977
*scope = LDAP_X_SCOPE_SUBTREE;
979
} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
980
bv.bv_val += STRLENOF( "onelevel:" );
981
*scope = LDAP_X_SCOPE_ONELEVEL;
984
return LDAP_PROTOCOL_ERROR;
988
if ( bv.bv_val[ 0 ] != ':' ) {
989
return LDAP_PROTOCOL_ERROR;
991
*scope = LDAP_X_SCOPE_EXACT;
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);
1000
/* a single '*' means any DN without using regexes */
1001
if ( ber_bvccmp( &bv, '*' ) ) {
1002
*scope = LDAP_X_SCOPE_USERS;
1006
case LDAP_X_SCOPE_EXACT:
1007
case LDAP_X_SCOPE_CHILDREN:
1008
case LDAP_X_SCOPE_SUBTREE:
1009
case LDAP_X_SCOPE_ONELEVEL:
1011
rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
1012
if( rc != LDAP_SUCCESS ) {
1016
ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1021
case LDAP_X_SCOPE_REGEX:
1022
ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1024
case LDAP_X_SCOPE_USERS:
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 ] == '.' ) )
1043
Connection c = *op->o_conn;
1044
char buf[ SLAP_LDAPDN_MAXLEN ];
1050
if ( sizeof( buf ) <= uri->bv_len ) {
1051
return LDAP_INVALID_SYNTAX;
1054
id.bv_len = uri->bv_len;
1056
strncpy( buf, uri->bv_val, sizeof( buf ) );
1058
rc = slap_parse_user( &id, &user, &realm, &mech );
1059
if ( rc != LDAP_SUCCESS ) {
1063
if ( !BER_BVISNULL( &mech ) ) {
1064
c.c_sasl_bind_mech = mech;
1066
BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1069
rc = slap_sasl_getdn( &c, op, &user,
1070
realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1072
if ( rc == LDAP_SUCCESS ) {
1073
*scope = LDAP_X_SCOPE_EXACT;
1079
* group[/<groupoc>[/<groupat>]]:<groupdn>
1081
* groupoc defaults to "groupOfNames"
1082
* groupat defaults to "member"
1084
* <groupdn> must pass DN normalization
1086
} else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1088
struct berval group_dn = BER_BVNULL,
1089
group_oc = BER_BVNULL,
1090
member_at = BER_BVNULL;
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;
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;
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;
1111
member_at.bv_len = group_dn.bv_val - member_at.bv_val;
1114
BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1118
BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
1119
BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1122
group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1125
rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1126
if ( rc != LDAP_SUCCESS ) {
1131
ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1134
*scope = LDAP_X_SCOPE_GROUP;
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 );
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( "=" ) );
1154
* ldap:///<base>??<scope>?<filter>
1155
* <scope> ::= {base|one|subtree}
1157
* <scope> defaults to "base"
1158
* <base> must pass DN normalization
1159
* <filter> must pass str2filter()
1161
rc = ldap_url_parse( uri->bv_val, &ludp );
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
1171
if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1175
rc = LDAP_PROTOCOL_ERROR;
1180
case LDAP_URL_ERR_BADSCHEME:
1182
* last chance: assume it's a(n exact) DN ...
1184
* NOTE: must pass DN normalization
1186
ldap_free_urldesc( ludp );
1187
bv.bv_val = uri->bv_val;
1188
*scope = LDAP_X_SCOPE_EXACT;
1192
rc = LDAP_PROTOCOL_ERROR;
1196
if ( ( ludp->lud_host && *ludp->lud_host )
1197
|| ludp->lud_attrs || ludp->lud_exts )
1199
/* host part must be empty */
1200
/* attrs and extensions parts must be empty */
1201
rc = LDAP_PROTOCOL_ERROR;
1205
/* Grab the scope */
1206
*scope = ludp->lud_scope;
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;
1215
ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1218
/* Grab the searchbase */
1219
ber_str2bv( ludp->lud_dn, 0, 0, base );
1221
rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1223
ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1228
if( rc != LDAP_SUCCESS ) {
1229
if( *filter ) filter_free_x( op, *filter );
1233
/* Don't free these, return them to caller */
1234
ludp->lud_filter = NULL;
1235
ludp->lud_dn = NULL;
1238
ldap_free_urldesc( ludp );
1242
#ifndef SLAP_AUTH_REWRITE
1243
static int slap_sasl_rx_off(char *rep, int *off)
1248
/* Precompile replace pattern. Find the $<n> placeholders */
1251
for ( c = rep; *c; c++ ) {
1252
if ( *c == '\\' && c[1] ) {
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 );
1263
return( LDAP_OTHER );
1270
/* Final placeholder, after the last $n */
1274
return( LDAP_SUCCESS );
1276
#endif /* ! SLAP_AUTH_REWRITE */
1278
#ifdef SLAP_AUTH_REWRITE
1279
int slap_sasl_rewrite_config(
1289
/* init at first call */
1290
if ( sasl_rwinfo == NULL ) {
1291
sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1294
/* strip "authid-" prefix for parsing */
1296
argv[0] += STRLENOF( "authid-" );
1297
rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1304
slap_sasl_rewrite_destroy( void )
1306
if ( sasl_rwinfo ) {
1307
rewrite_info_delete( &sasl_rwinfo );
1314
int slap_sasl_regexp_rewrite_config(
1318
const char *replace,
1319
const char *context )
1322
char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1324
/* init at first call */
1325
if ( sasl_rwinfo == NULL ) {
1326
char *argvEngine[] = { "rewriteEngine", "on", NULL };
1327
char *argvContext[] = { "rewriteContext", NULL, NULL };
1329
/* initialize rewrite engine */
1330
sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1332
/* switch on rewrite engine */
1333
rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1334
if (rc != LDAP_SUCCESS) {
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) {
1346
argvRule[1] = (char *)match;
1347
argvRule[2] = (char *)replace;
1348
rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1352
#endif /* SLAP_AUTH_REWRITE */
1354
int slap_sasl_regexp_config( const char *match, const char *replace )
1359
SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1360
(nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1362
reg = &SaslRegexp[nSaslRegexp];
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 */
1369
/* Precompile matching pattern */
1370
rc = regcomp( ®->sr_workspace, match, REG_EXTENDED|REG_ICASE );
1372
Debug( LDAP_DEBUG_ANY,
1373
"SASL match pattern %s could not be compiled by regexp engine\n",
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 );
1382
return( LDAP_OTHER );
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 );
1398
slap_sasl_regexp_destroy( void )
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 */
1411
ch_free( SaslRegexp );
1414
#ifdef SLAP_AUTH_REWRITE
1415
slap_sasl_rewrite_destroy();
1416
#endif /* SLAP_AUTH_REWRITE */
1419
void slap_sasl_regexp_unparse( BerVarray *out )
1422
BerVarray bva = NULL;
1423
char ibuf[32], *ptr;
1426
if ( !nSaslRegexp ) return;
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 );
1438
ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1439
ptr = lutil_strcopy( ptr, "\" \"" );
1440
ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1447
#ifndef SLAP_AUTH_REWRITE
1448
/* Perform replacement on regexp matches */
1449
static void slap_sasl_rx_exp(
1453
const char *saslname,
1457
int i, n, len, insert;
1459
/* Get the total length of the final URI */
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;
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;
1474
out->bv_val = slap_sl_malloc( len + 1, ctx );
1477
/* Fill in URI with replace string, replacing $i as we go */
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);
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 );
1497
out->bv_val[insert] = '\0';
1499
#endif /* ! SLAP_AUTH_REWRITE */
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) */
1505
static int slap_authz_regexp( struct berval *in, struct berval *out,
1506
int flags, void *ctx )
1508
#ifdef SLAP_AUTH_REWRITE
1509
const char *context = AUTHID_CONTEXT;
1511
if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
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,
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 ) {
1528
ber_dupbv_x( out, in, ctx );
1530
Debug( LDAP_DEBUG_ARGS,
1531
"[rw] %s: \"%s\" -> \"%s\"\n",
1532
context, in->bv_val, out->bv_val );
1535
case REWRITE_REGEXEC_UNWILLING:
1536
case REWRITE_REGEXEC_ERR:
1541
#else /* ! SLAP_AUTH_REWRITE */
1542
char *saslname = in->bv_val;
1544
regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
1547
memset( out, 0, sizeof( *out ) );
1549
Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1552
if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
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 )
1563
if( i >= nSaslRegexp ) return( 0 );
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.*)
1570
slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1571
sr_strings, saslname, out, ctx );
1573
Debug( LDAP_DEBUG_TRACE,
1574
"slap_authz_regexp: converted SASL name to %s\n",
1575
BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1578
#endif /* ! SLAP_AUTH_REWRITE */
1581
/* This callback actually does some work...*/
1582
static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1584
struct berval *ndn = op->o_callback->sc_private;
1586
if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1588
/* We only want to be called once */
1589
if ( !BER_BVISNULL( ndn ) ) {
1590
op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
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 */
1599
ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1600
return LDAP_SUCCESS;
1604
typedef struct smatch_info {
1609
static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1611
smatch_info *sm = o->o_callback->sc_private;
1613
if (rs->sr_type != REP_SEARCH) return 0;
1615
if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1617
return LDAP_UNAVAILABLE; /* short-circuit the search */
1624
slap_sasl_matches( Operation *op, BerVarray rules,
1625
struct berval *assertDN, struct berval *authc )
1627
int rc = LDAP_INAPPROPRIATE_AUTH;
1629
if ( rules != NULL ) {
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;
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.
1647
* The assertDN should not have the dn: prefix
1651
slap_sasl_match( Operation *opx, struct berval *rule,
1652
struct berval *assertDN, struct berval *authc )
1657
slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1659
SlapReply rs = {REP_RESULT};
1660
struct berval base = BER_BVNULL;
1664
cb.sc_private = &sm;
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 );
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 );
1674
if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1676
switch ( op.ors_scope ) {
1677
case LDAP_X_SCOPE_EXACT:
1679
if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1682
rc = LDAP_INAPPROPRIATE_AUTH;
1686
case LDAP_X_SCOPE_CHILDREN:
1687
case LDAP_X_SCOPE_SUBTREE:
1688
case LDAP_X_SCOPE_ONELEVEL:
1690
int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1692
rc = LDAP_INAPPROPRIATE_AUTH;
1694
if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1697
} else if ( d > 0 ) {
1700
/* leave room for at least one char of attributeType,
1701
* one for '=' and one for ',' */
1702
if ( d < STRLENOF( "x=,") ) {
1706
bv.bv_len = op.o_req_ndn.bv_len;
1707
bv.bv_val = assertDN->bv_val + d;
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:
1716
case LDAP_X_SCOPE_ONELEVEL:
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 ) {
1731
/* at present, impossible */
1739
case LDAP_X_SCOPE_REGEX:
1740
rc = regcomp(®, op.o_req_ndn.bv_val,
1741
REG_EXTENDED|REG_ICASE|REG_NOSUB);
1743
rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1749
rc = LDAP_INAPPROPRIATE_AUTH;
1753
case LDAP_X_SCOPE_GROUP: {
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;
1766
op.ors_filterstr.bv_val = tmp;
1768
tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1769
tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
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;
1777
op.ors_scope = LDAP_SCOPE_BASE;
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;
1787
case LDAP_X_SCOPE_USERS:
1788
if ( !BER_BVISEMPTY( assertDN ) ) {
1791
rc = LDAP_INAPPROPRIATE_AUTH;
1799
/* Must run an internal search. */
1800
if ( op.ors_filter == NULL ) {
1801
rc = LDAP_FILTER_ERROR;
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 );
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;
1815
op.o_hdr = opx->o_hdr;
1816
op.o_tag = LDAP_REQ_SEARCH;
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 );
1828
ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1829
op.ors_deref = LDAP_DEREF_NEVER;
1831
op.ors_tlimit = SLAP_NO_LIMIT;
1832
op.ors_attrs = slap_anlist_no_attrs;
1833
op.ors_attrsonly = 1;
1835
op.o_bd->be_search( &op, &rs );
1840
rc = LDAP_INAPPROPRIATE_AUTH;
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 );
1849
Debug( LDAP_DEBUG_TRACE,
1850
"<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
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.
1862
* The DNs should not have the dn: prefix
1865
slap_sasl_check_authz( Operation *op,
1866
struct berval *searchDN,
1867
struct berval *assertDN,
1868
AttributeDescription *ad,
1869
struct berval *authc )
1872
do_not_cache = op->o_do_not_cache;
1873
BerVarray vals = NULL;
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);
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;
1885
/* Check if the *assertDN matches any *vals */
1886
rc = slap_sasl_matches( op, vals, assertDN, authc );
1889
if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1891
Debug( LDAP_DEBUG_TRACE,
1892
"<==slap_sasl_check_authz: %s check returning %d\n",
1893
ad->ad_cname.bv_val, rc, 0);
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.
1909
struct berval *saslname,
1910
struct berval *sasldn,
1914
slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1916
SlapReply rs = {REP_RESULT};
1917
struct berval regout = BER_BVNULL;
1918
struct berval base = BER_BVNULL;
1920
Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1921
"converting SASL name %s to a DN\n",
1922
saslname->bv_val, 0,0 );
1924
BER_BVZERO( sasldn );
1925
cb.sc_private = sasldn;
1927
/* Convert the SASL name into a minimal URI */
1928
if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
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 ) {
1941
/* Must do an internal search */
1942
op.o_bd = select_backend( &op.o_req_ndn, 1 );
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 */
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 */
1959
case LDAP_SCOPE_BASE:
1960
case LDAP_SCOPE_ONELEVEL:
1961
case LDAP_SCOPE_SUBTREE:
1962
case LDAP_SCOPE_SUBORDINATE:
1967
/* catch unhandled cases (there shouldn't be) */
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 );
1975
if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1979
/* Must run an internal search. */
1980
if ( op.ors_filter == NULL ) {
1981
rc = LDAP_FILTER_ERROR;
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;
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 );
2003
ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2005
op.o_bd->be_search( &op, &rs );
2008
if( !BER_BVISEMPTY( sasldn ) ) {
2009
opx->o_conn->c_authz_backend = op.o_bd;
2011
if( !BER_BVISNULL( &op.o_req_dn ) ) {
2012
slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2014
if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2015
slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2017
if( op.ors_filter ) {
2018
filter_free_x( opx, op.ors_filter );
2020
if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2021
ch_free( op.ors_filterstr.bv_val );
2024
Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2025
!BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2031
/* Check if a bind can SASL authorize to another identity.
2032
* The DNs should not have the dn: prefix
2035
int slap_sasl_authorized( Operation *op,
2036
struct berval *authcDN, struct berval *authzDN )
2038
int rc = LDAP_INAPPROPRIATE_AUTH;
2040
/* User binding as anonymous */
2041
if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) {
2046
/* User is anonymous */
2047
if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) {
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 );
2056
/* If person is authorizing to self, succeed */
2057
if ( dn_match( authcDN, authzDN ) ) {
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 ))
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) ) {
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 ) {
2088
rc = LDAP_INAPPROPRIATE_AUTH;
2092
Debug( LDAP_DEBUG_TRACE,
2093
"<== slap_sasl_authorized: return %d\n", rc, 0, 0 );