]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/bind.c
fix server-size controls handling in back-{ldap,meta}
[openldap] / servers / slapd / back-meta / bind.c
1 /*
2  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
3  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
4  *
5  * Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
6  *
7  * This work has been developed to fulfill the requirements
8  * of SysNet s.n.c. <http:www.sys-net.it> and it has been donated
9  * to the OpenLDAP Foundation in the hope that it may be useful
10  * to the Open Source community, but WITHOUT ANY WARRANTY.
11  *
12  * Permission is granted to anyone to use this software for any purpose
13  * on any computer system, and to alter it and redistribute it, subject
14  * to the following restrictions:
15  *
16  * 1. The author and SysNet s.n.c. are not responsible for the consequences
17  *    of use of this software, no matter how awful, even if they arise from 
18  *    flaws in it.
19  *
20  * 2. The origin of this software must not be misrepresented, either by
21  *    explicit claim or by omission.  Since few users ever read sources,
22  *    credits should appear in the documentation.
23  *
24  * 3. Altered versions must be plainly marked as such, and must not be
25  *    misrepresented as being the original software.  Since few users
26  *    ever read sources, credits should appear in the documentation.
27  *    SysNet s.n.c. cannot be responsible for the consequences of the
28  *    alterations.
29  *
30  * 4. This notice may not be removed or altered.
31  *
32  *
33  * This software is based on the backend back-ldap, implemented
34  * by Howard Chu <hyc@highlandsun.com>, and modified by Mark Valence
35  * <kurash@sassafras.com>, Pierangelo Masarati <ando@sys-net.it> and other
36  * contributors. The contribution of the original software to the present
37  * implementation is acknowledged in this copyright statement.
38  *
39  * A special acknowledgement goes to Howard for the overall architecture
40  * (and for borrowing large pieces of code), and to Mark, who implemented
41  * from scratch the attribute/objectclass mapping.
42  *
43  * The original copyright statement follows.
44  *
45  * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
46  *
47  * Permission is granted to anyone to use this software for any purpose
48  * on any computer system, and to alter it and redistribute it, subject
49  * to the following restrictions:
50  *
51  * 1. The author is not responsible for the consequences of use of this
52  *    software, no matter how awful, even if they arise from flaws in it.
53  *
54  * 2. The origin of this software must not be misrepresented, either by
55  *    explicit claim or by omission.  Since few users ever read sources,
56  *    credits should appear in the documentation.
57  *
58  * 3. Altered versions must be plainly marked as such, and must not be
59  *    misrepresented as being the original software.  Since few users
60  *    ever read sources, credits should appear in the
61  *    documentation.
62  *
63  * 4. This notice may not be removed or altered.
64  *
65  */
66
67 #include "portable.h"
68
69 #include <stdio.h>
70
71 #include <ac/socket.h>
72 #include <ac/string.h>
73
74
75 #define AVL_INTERNAL
76 #include "slap.h"
77 #include "../back-ldap/back-ldap.h"
78 #include "back-meta.h"
79
80 static int
81 meta_back_do_single_bind(
82                 struct metainfo         *li,
83                 struct metaconn         *lc,
84                 Operation               *op,
85                 struct berval           *dn,
86                 struct berval           *ndn,
87                 struct berval           *cred,
88                 int                     method,
89                 int                     candidate
90 );
91
92 int
93 meta_back_bind(
94                 Backend         *be,
95                 Connection      *conn,
96                 Operation       *op,
97                 struct berval   *dn,
98                 struct berval   *ndn,
99                 int             method,
100                 struct berval   *cred,
101                 struct berval   *edn
102 )
103 {
104         struct metainfo *li = ( struct metainfo * )be->be_private;
105         struct metaconn *lc;
106
107         int rc = -1, i, gotit = 0, ndnlen, isroot = 0;
108         int op_type = META_OP_ALLOW_MULTIPLE;
109         int err = LDAP_SUCCESS;
110
111         struct berval *realdn = dn;
112         struct berval *realndn = ndn;
113         struct berval *realcred = cred;
114         int realmethod = method;
115
116 #ifdef NEW_LOGGING
117         LDAP_LOG( BACK_META, ENTRY,
118                         "meta_back_bind: dn: %s.\n", dn->bv_val, 0, 0 );
119 #else /* !NEW_LOGGING */
120         Debug( LDAP_DEBUG_ARGS, "meta_back_bind: dn: %s.\n%s%s", dn->bv_val, "", "" );
121 #endif /* !NEW_LOGGING */
122
123         if ( method == LDAP_AUTH_SIMPLE 
124                         && be_isroot_pw( be, conn, ndn, cred ) ) {
125                 isroot = 1;
126                 ber_dupbv( edn, be_root_dn( be ) );
127                 op_type = META_OP_REQUIRE_ALL;
128         }
129         lc = meta_back_getconn( li, conn, op, op_type, ndn, NULL );
130         if ( !lc ) {
131 #ifdef NEW_LOGGING
132                 LDAP_LOG( BACK_META, NOTICE,
133                                 "meta_back_bind: no target for dn %s.\n", dn->bv_val, 0, 0 );
134 #else /* !NEW_LOGGING */
135                 Debug( LDAP_DEBUG_ANY,
136                                 "meta_back_bind: no target for dn %s.\n%s%s",
137                                 dn->bv_val, "", "");
138 #endif /* !NEW_LOGGING */
139                 send_ldap_result( conn, op, LDAP_OTHER, 
140                                 NULL, NULL, NULL, NULL );
141                 return -1;
142         }
143
144         /*
145          * Each target is scanned ...
146          */
147         lc->bound_target = META_BOUND_NONE;
148         ndnlen = ndn->bv_len;
149         for ( i = 0; i < li->ntargets; i++ ) {
150                 int lerr;
151
152                 /*
153                  * Skip non-candidates
154                  */
155                 if ( lc->conns[ i ].candidate != META_CANDIDATE ) {
156                         continue;
157                 }
158
159                 if ( gotit == 0 ) {
160                         gotit = 1;
161                 } else {
162                         /*
163                          * A bind operation is expected to have
164                          * ONE CANDIDATE ONLY!
165                          */
166 #ifdef NEW_LOGGING
167                         LDAP_LOG( BACK_META, WARNING,
168                                         "==>meta_back_bind: more than one"
169                                         " candidate is attempting to bind"
170                                         " ...\n" , 0, 0, 0 );
171 #else /* !NEW_LOGGING */
172                         Debug( LDAP_DEBUG_ANY,
173                                         "==>meta_back_bind: more than one"
174                                         " candidate is attempting to bind"
175                                         " ...\n%s%s%s", 
176                                         "", "", "" );
177 #endif /* !NEW_LOGGING */
178                 }
179
180                 if ( isroot && li->targets[ i ]->pseudorootdn.bv_val != NULL ) {
181                         realdn = &li->targets[ i ]->pseudorootdn;
182                         realndn = &li->targets[ i ]->pseudorootdn;
183                         realcred = &li->targets[ i ]->pseudorootpw;
184                         realmethod = LDAP_AUTH_SIMPLE;
185                 } else {
186                         realdn = dn;
187                         realndn = ndn;
188                         realcred = cred;
189                         realmethod = method;
190                 }
191                 
192                 lerr = meta_back_do_single_bind( li, lc, op,
193                                 realdn, realndn, realcred, realmethod, i );
194                 if ( lerr != LDAP_SUCCESS ) {
195                         err = lerr;
196                         ( void )meta_clear_one_candidate( &lc->conns[ i ], 1 );
197                 } else {
198                         rc = LDAP_SUCCESS;
199                 }
200         }
201
202         if ( isroot ) {
203                 lc->bound_target = META_BOUND_ALL;
204         }
205
206         /*
207          * rc is LDAP_SUCCESS if at least one bind succeeded,
208          * err is the last error that occurred during a bind;
209          * if at least (and at most?) one bind succeedes, fine.
210          */
211         if ( rc != LDAP_SUCCESS /* && err != LDAP_SUCCESS */ ) {
212                 
213                 /*
214                  * deal with bind failure ...
215                  */
216
217                 /*
218                  * no target was found within the naming context, 
219                  * so bind must fail with invalid credentials
220                  */
221                 if ( err == LDAP_SUCCESS && gotit == 0 ) {
222                         err = LDAP_INVALID_CREDENTIALS;
223                 }
224
225                 err = ldap_back_map_result( err );
226                 send_ldap_result( conn, op, err, NULL, NULL, NULL, NULL );
227                 return -1;
228         }
229
230         return 0;
231 }
232
233 /*
234  * meta_back_do_single_bind
235  *
236  * attempts to perform a bind with creds
237  */
238 static int
239 meta_back_do_single_bind(
240                 struct metainfo         *li,
241                 struct metaconn         *lc,
242                 Operation               *op,
243                 struct berval           *dn,
244                 struct berval           *ndn,
245                 struct berval           *cred,
246                 int                     method,
247                 int                     candidate
248 )
249 {
250         struct berval mdn = { 0, NULL };
251         int rc;
252         
253         /*
254          * Rewrite the bind dn if needed
255          */
256         switch ( rewrite_session( li->targets[ candidate ]->rwinfo,
257                                 "bindDn", dn->bv_val, lc->conn, &mdn.bv_val ) ) {
258         case REWRITE_REGEXEC_OK:
259                 if ( mdn.bv_val == NULL ) {
260                         mdn = *dn;
261                 }
262 #ifdef NEW_LOGGING
263                 LDAP_LOG( BACK_META, DETAIL1,
264                                 "[rw] bindDn: \"%s\" -> \"%s\"\n", dn->bv_val, mdn.bv_val, 0 );
265 #else /* !NEW_LOGGING */
266                 Debug( LDAP_DEBUG_ARGS,
267                                 "rw> bindDn: \"%s\" -> \"%s\"\n%s",
268                                 dn->bv_val, mdn.bv_val, "" );
269 #endif /* !NEW_LOGGING */
270                 break;
271                 
272         case REWRITE_REGEXEC_UNWILLING:
273                 return LDAP_UNWILLING_TO_PERFORM;
274
275         case REWRITE_REGEXEC_ERR:
276                 return LDAP_OTHER;
277         }
278
279         if ( op->o_ctrls ) {
280                 rc = ldap_set_option( lc->conns[ candidate ].ld, 
281                                 LDAP_OPT_SERVER_CONTROLS, op->o_ctrls );
282                 if ( rc != LDAP_SUCCESS ) {
283                         rc = ldap_back_map_result( rc );
284                         goto return_results;
285                 }
286         }
287         
288         rc = ldap_bind_s( lc->conns[ candidate ].ld, mdn.bv_val, cred->bv_val, method );
289         if ( rc != LDAP_SUCCESS ) {
290                 rc = ldap_back_map_result( rc );
291         } else {
292                 ber_dupbv( &lc->conns[ candidate ].bound_dn, dn );
293                 lc->conns[ candidate ].bound = META_BOUND;
294                 lc->bound_target = candidate;
295
296                 if ( li->cache.ttl != META_DNCACHE_DISABLED
297                                 && ndn->bv_len != 0 ) {
298                         ( void )meta_dncache_update_entry( &li->cache,
299                                         ndn, candidate );
300                 }
301         }
302
303 return_results:;
304         
305         if ( mdn.bv_val != dn->bv_val ) {
306                 free( mdn.bv_val );
307         }
308
309         return rc;
310 }
311
312 /*
313  * meta_back_dobind
314  */
315 int
316 meta_back_dobind( struct metaconn *lc, Operation *op )
317 {
318         struct metasingleconn *lsc;
319         int bound = 0, i;
320
321         /*
322          * all the targets are bound as pseudoroot
323          */
324         if ( lc->bound_target == META_BOUND_ALL ) {
325                 return 1;
326         }
327
328         for ( i = 0, lsc = lc->conns; !META_LAST(lsc); ++i, ++lsc ) {
329                 int rc;
330
331                 /*
332                  * Not a candidate or something wrong with this target ...
333                  */
334                 if ( lsc->ld == NULL ) {
335                         continue;
336                 }
337
338                 /*
339                  * If required, set controls
340                  */
341                 if ( op->o_ctrls ) {
342                         if ( ldap_set_option( lsc->ld, LDAP_OPT_SERVER_CONTROLS,
343                                         op->o_ctrls ) != LDAP_SUCCESS ) {
344                                 ( void )meta_clear_one_candidate( lsc, 1 );
345                                 continue;
346                         }
347                 }
348         
349                 /*
350                  * If the target is already bound it is skipped
351                  */
352                 if ( lsc->bound == META_BOUND && lc->bound_target == i ) {
353                         ++bound;
354                         continue;
355                 }
356
357                 /*
358                  * Otherwise an anonymous bind is performed
359                  * (note: if the target was already bound, the anonymous
360                  * bind clears the previous bind).
361                  */
362                 if ( lsc->bound_dn.bv_val ) {
363                         ch_free( lsc->bound_dn.bv_val );
364                         lsc->bound_dn.bv_val = NULL;
365                         lsc->bound_dn.bv_len = 0;
366                 }
367                 
368
369                 rc = ldap_bind_s( lsc->ld, 0, NULL, LDAP_AUTH_SIMPLE );
370                 if ( rc != LDAP_SUCCESS ) {
371                         
372 #ifdef NEW_LOGGING
373                         LDAP_LOG( BACK_META, WARNING,
374                                         "meta_back_dobind: (anonymous)"
375                                         " bind as \"%s\" failed"
376                                         " with error \"%s\"\n",
377                                         lsc->bound_dn.bv_val,
378                                         ldap_err2string( rc ), 0 );
379 #else /* !NEW_LOGGING */
380                         Debug( LDAP_DEBUG_ANY,
381                                         "==>meta_back_dobind: (anonymous)"
382                                         " bind as \"%s\" failed"
383                                         " with error \"%s\"\n%s",
384                                         lsc->bound_dn.bv_val,
385                                         ldap_err2string( rc ), "" );
386 #endif /* !NEW_LOGGING */
387
388                         /*
389                          * null cred bind should always succeed
390                          * as anonymous, so a failure means
391                          * the target is no longer candidate possibly
392                          * due to technical reasons (remote host down?)
393                          * so better clear the handle
394                          */
395                         ( void )meta_clear_one_candidate( lsc, 1 );
396                         continue;
397                 } /* else */
398                 
399                 lsc->bound = META_ANONYMOUS;
400                 ++bound;
401         }
402
403         return( bound > 0 );
404 }
405
406 /*
407  *
408  */
409 int
410 meta_back_is_valid( struct metaconn *lc, int candidate )
411 {
412         struct metasingleconn   *lsc;
413         int                     i;
414
415         assert( lc );
416
417         if ( candidate < 0 ) {
418                 return 0;
419         }
420
421         for ( i = 0, lsc = lc->conns; !META_LAST(lsc) && i < candidate; 
422                         ++i, ++lsc );
423         
424         if ( !META_LAST(lsc) ) {
425                 return( lsc->ld != NULL );
426         }
427
428         return 0;
429 }
430
431 /*
432  * FIXME: error return must be handled in a cleaner way ...
433  */
434 int
435 meta_back_op_result( struct metaconn *lc, Operation *op )
436 {
437         int i, rerr = LDAP_SUCCESS;
438         struct metasingleconn *lsc;
439         char *rmsg = NULL;
440         char *rmatch = NULL;
441
442         for ( i = 0, lsc = lc->conns; !META_LAST(lsc); ++i, ++lsc ) {
443                 int err = LDAP_SUCCESS;
444                 char *msg = NULL;
445                 char *match = NULL;
446
447                 ldap_get_option( lsc->ld, LDAP_OPT_ERROR_NUMBER, &err );
448                 if ( err != LDAP_SUCCESS ) {
449                         /*
450                          * better check the type of error. In some cases
451                          * (search ?) it might be better to return a
452                          * success if at least one of the targets gave
453                          * positive result ...
454                          */
455                         ldap_get_option( lsc->ld,
456                                         LDAP_OPT_ERROR_STRING, &msg );
457                         ldap_get_option( lsc->ld,
458                                         LDAP_OPT_MATCHED_DN, &match );
459                         err = ldap_back_map_result( err );
460
461 #ifdef NEW_LOGGING
462                         LDAP_LOG( BACK_META, RESULTS,
463                                         "meta_back_op_result: target"
464                                         " <%d> sending msg \"%s\""
465                                         " (matched \"%s\")\n",
466                                         i, ( msg ? msg : "" ),
467                                         ( match ? match : "" ) );
468 #else /* !NEW_LOGGING */
469                         Debug(LDAP_DEBUG_ANY,
470                                         "==> meta_back_op_result: target"
471                                         " <%d> sending msg \"%s\""
472                                         " (matched \"%s\")\n", 
473                                         i, ( msg ? msg : "" ),
474                                         ( match ? match : "" ) );
475 #endif /* !NEW_LOGGING */
476
477                         /*
478                          * FIXME: need to rewrite "match" (need rwinfo)
479                          */
480                         switch ( err ) {
481                         default:
482                                 rerr = err;
483                                 rmsg = msg;
484                                 msg = NULL;
485                                 rmatch = match;
486                                 match = NULL;
487                                 break;
488                         }
489
490                         /* better test the pointers before freeing? */
491                         if ( match ) {
492                                 free( match );
493                         }
494                         if ( msg ) {
495                                 free( msg );
496                         }
497                 }
498         }
499
500         send_ldap_result( lc->conn, op, rerr, rmatch, rmsg, NULL, NULL );
501
502         return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );
503 }
504