~ttx/openldap/lucid-gssapi-495418

« back to all changes in this revision

Viewing changes to servers/slapd/saslauthz.c

  • Committer: Bazaar Package Importer
  • Author(s): Mathias Gug
  • Date: 2008-07-10 14:45:49 UTC
  • Revision ID: james.westby@ubuntu.com-20080710144549-wck73med0e72gfyo
Tags: upstream-2.4.10
ImportĀ upstreamĀ versionĀ 2.4.10

Show diffs side-by-side

added added

removed removed

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