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