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