]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/bind.c
add basic support for retry; protect internal bind behind mutexes; rework search...
[openldap] / servers / slapd / back-meta / bind.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2005 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/socket.h>
28 #include <ac/string.h>
29
30
31 #define AVL_INTERNAL
32 #include "slap.h"
33 #include "../back-ldap/back-ldap.h"
34 #include "back-meta.h"
35
36 static LDAP_REBIND_PROC meta_back_rebind;
37
38 static int
39 meta_back_single_bind(
40                 Operation               *op,
41                 SlapReply               *rs,
42                 struct metaconn         *mc,
43                 int                     candidate );
44
45 int
46 meta_back_bind( Operation *op, SlapReply *rs )
47 {
48         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
49         struct metaconn *mc;
50
51         int             rc = LDAP_OTHER,
52                         i, gotit = 0, isroot = 0;
53
54         char            *candidates = meta_back_candidates_get( op );
55
56         rs->sr_err = LDAP_SUCCESS;
57
58         Debug( LDAP_DEBUG_ARGS, "meta_back_bind: dn: %s.\n%s%s",
59                         op->o_req_dn.bv_val, "", "" );
60
61         if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
62                 isroot = 1;
63                 ber_dupbv( &op->orb_edn, be_root_dn( op->o_bd ) );
64         }
65
66         /* we need meta_back_getconn() not send result even on error,
67          * because we want to intercept the error and make it
68          * invalidCredentials */
69         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_DONTSEND );
70         if ( !mc ) {
71                 Debug( LDAP_DEBUG_ANY,
72                                 "meta_back_bind: no target "
73                                 "for dn \"%s\" (%d: %s).\n",
74                                 op->o_req_dn.bv_val, rs->sr_err,
75                                 rs->sr_text ? rs->sr_text : "" );
76                 /* FIXME: there might be cases where we don't want
77                  * to map the error onto invalidCredentials */
78                 switch ( rs->sr_err ) {
79                 case LDAP_NO_SUCH_OBJECT:
80                 case LDAP_UNWILLING_TO_PERFORM:
81                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
82                         rs->sr_text = NULL;
83                         break;
84                 }
85                 send_ldap_result( op, rs );
86                 return rs->sr_err;
87         }
88
89         /*
90          * Each target is scanned ...
91          */
92         mc->mc_auth_target = META_BOUND_NONE;
93         for ( i = 0; i < li->mi_ntargets; i++ ) {
94                 int             lerr;
95                 Operation       op2 = *op;
96
97                 /*
98                  * Skip non-candidates
99                  */
100                 if ( candidates[ i ] != META_CANDIDATE ) {
101                         continue;
102                 }
103
104                 if ( gotit == 0 ) {
105                         gotit = 1;
106
107                 } else if ( isroot == 0 ) {
108                         /*
109                          * A bind operation is expected to have
110                          * ONE CANDIDATE ONLY!
111                          */
112                         Debug( LDAP_DEBUG_ANY,
113                                         "==>meta_back_bind: more than one"
114                                         " candidate is trying to bind...\n",
115                                         0, 0, 0 );
116                 }
117
118                 if ( isroot && !BER_BVISNULL( &li->mi_targets[ i ]->mt_pseudorootdn ) )
119                 {
120                         op2.o_req_dn = li->mi_targets[ i ]->mt_pseudorootdn;
121                         op2.o_req_ndn = li->mi_targets[ i ]->mt_pseudorootdn;
122                         op2.orb_cred = li->mi_targets[ i ]->mt_pseudorootpw;
123                         op2.orb_method = LDAP_AUTH_SIMPLE;
124                 }
125                 
126                 lerr = meta_back_single_bind( &op2, rs, mc, i );
127                 if ( lerr != LDAP_SUCCESS ) {
128                         rs->sr_err = lerr;
129                         candidates[ i ] = META_NOT_CANDIDATE;
130
131                 } else {
132                         rc = LDAP_SUCCESS;
133                 }
134         }
135
136         if ( isroot ) {
137                 mc->mc_auth_target = META_BOUND_ALL;
138         }
139
140         /*
141          * rc is LDAP_SUCCESS if at least one bind succeeded,
142          * err is the last error that occurred during a bind;
143          * if at least (and at most?) one bind succeedes, fine.
144          */
145         if ( rc != LDAP_SUCCESS ) {
146                 
147                 /*
148                  * deal with bind failure ...
149                  */
150
151                 /*
152                  * no target was found within the naming context, 
153                  * so bind must fail with invalid credentials
154                  */
155                 if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
156                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
157                 }
158
159                 rs->sr_err = slap_map_api2result( rs );
160                 send_ldap_result( op, rs );
161                 return rs->sr_err;
162         }
163
164         return LDAP_SUCCESS;
165 }
166
167 /*
168  * meta_back_single_bind
169  *
170  * attempts to perform a bind with creds
171  */
172 static int
173 meta_back_single_bind(
174                 Operation               *op,
175                 SlapReply               *rs,
176                 struct metaconn         *mc,
177                 int                     candidate
178 )
179 {
180         struct metainfo         *li = ( struct metainfo * )op->o_bd->be_private;
181         struct berval           mdn = BER_BVNULL;
182         dncookie                dc;
183         struct metasingleconn   *msc = &mc->mc_conns[ candidate ];
184         int                     msgid;
185         
186         /*
187          * Rewrite the bind dn if needed
188          */
189         dc.rwmap = &li->mi_targets[ candidate ]->mt_rwmap;
190         dc.conn = op->o_conn;
191         dc.rs = rs;
192         dc.ctx = "bindDN";
193
194         if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
195                 send_ldap_result( op, rs );
196                 return -1;
197         }
198
199         /* FIXME: this fixes the bind problem right now; we need
200          * to use the asynchronous version to get the "matched"
201          * and more in case of failure ... */
202         /* FIXME: should be check if at least some of the op->o_ctrls
203          * can/should be passed? */
204         rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
205                         LDAP_SASL_SIMPLE, &op->orb_cred,
206                         op->o_ctrls, NULL, &msgid );
207         if ( rs->sr_err == LDAP_SUCCESS ) {
208                 LDAPMessage     *res;
209                 struct timeval  tv = { 0, 0 };
210                 int             rc;
211                 int             nretries = META_BIND_NRETRIES;
212
213                 /*
214                  * handle response!!!
215                  */
216 retry:;
217                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
218                 case 0:
219                         if ( nretries > 0 ) {
220                                 ldap_pvt_thread_yield();
221                                 tv.tv_sec = 0;
222                                 tv.tv_usec = META_BIND_TIMEOUT;
223                                 nretries--;
224                                 goto retry;
225                         }
226                         rs->sr_err = LDAP_BUSY;
227                         break;
228
229                 case -1:
230                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
231                                         &rs->sr_err );
232                         if ( rs->sr_err == LDAP_UNAVAILABLE && nretries > 0 ) {
233                                 nretries--;
234                                 if ( meta_back_retry( op, rs, mc, candidate, LDAP_BACK_SENDERR ) ) {
235                                         goto retry;
236                                 }
237                         }
238                         break;
239
240                 default:
241                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
242                                         NULL, NULL, NULL, NULL, 1 );
243                         if ( rc != LDAP_SUCCESS ) {
244                                 rs->sr_err = rc;
245                         }
246                         break;
247                 }
248         }
249
250         if ( rs->sr_err != LDAP_SUCCESS ) {
251                 rs->sr_err = slap_map_api2result( rs );
252                 goto return_results;
253         }
254
255         ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_dn );
256         msc->msc_bound = META_BOUND;
257         mc->mc_auth_target = candidate;
258
259         if ( LDAP_BACK_SAVECRED( li ) ) {
260                 ber_bvreplace( &msc->msc_cred, &op->orb_cred );
261                 ldap_set_rebind_proc( msc->msc_ld, meta_back_rebind, msc );
262         }
263
264         if ( li->mi_cache.ttl != META_DNCACHE_DISABLED
265                         && op->o_req_ndn.bv_len != 0 )
266         {
267                 ( void )meta_dncache_update_entry( &li->mi_cache,
268                                 &op->o_req_ndn, candidate );
269         }
270
271 return_results:;
272         if ( mdn.bv_val != op->o_req_dn.bv_val ) {
273                 free( mdn.bv_val );
274         }
275
276         return rs->sr_err;
277 }
278
279 /*
280  * meta_back_single_dobind
281  */
282 int
283 meta_back_single_dobind(
284         Operation               *op,
285         struct metasingleconn   *msc,
286         ldap_back_send_t        sendok,
287         int                     retries )
288 {
289         int             rc;
290         struct berval   cred = BER_BVC( "" );
291         int             msgid;
292
293         /*
294          * Otherwise an anonymous bind is performed
295          * (note: if the target was already bound, the anonymous
296          * bind clears the previous bind).
297          */
298         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
299                 ber_memfree( msc->msc_bound_ndn.bv_val );
300                 BER_BVZERO( &msc->msc_bound_ndn );
301         }
302                 
303         if ( /* FIXME: need li ... li->savecred && */ 
304                         !BER_BVISNULL( &msc->msc_cred ) )
305         {
306                 /* destroy sensitive data */
307                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
308                 ber_memfree( msc->msc_cred.bv_val );
309                 BER_BVZERO( &msc->msc_cred );
310         }
311
312         /* FIXME: should we check if at least some of the op->o_ctrls
313          * can/should be passed? */
314         rc = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
315                         NULL, NULL, &msgid );
316         if ( rc == LDAP_SUCCESS ) {
317                 LDAPMessage     *res;
318                 struct timeval  tv = { 0, 0 };
319                 int             err;
320
321                 /*
322                  * handle response!!!
323                  */
324 retry:;
325                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
326                 case 0:
327                         if ( retries > 0 ) {
328                                 ldap_pvt_thread_yield();
329                                 tv.tv_sec = 0;
330                                 tv.tv_usec = META_BIND_TIMEOUT;
331                                 retries--;
332                                 goto retry;
333                         }
334
335                         rc = LDAP_BUSY;
336                         break;
337
338                 case -1:
339                         ldap_get_option( msc->msc_ld,
340                                         LDAP_OPT_ERROR_NUMBER, &rc );
341                         break;
342
343                 default:
344                         rc = ldap_parse_result( msc->msc_ld, res, &err,
345                                         NULL, NULL, NULL, NULL, 1 );
346                         if ( rc == LDAP_SUCCESS ) {
347                                 rc = err;
348                         }
349                         break;
350                 }
351         }
352
353         return rc;
354 }
355
356 /*
357  * meta_back_dobind
358  */
359 int
360 meta_back_dobind( struct metaconn *mc, Operation *op, ldap_back_send_t sendok )
361 {
362         struct metasingleconn   *msc;
363         int                     bound = 0, i;
364
365         char                    *candidates = meta_back_candidates_get( op );
366
367         ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
368
369         /*
370          * all the targets are bound as pseudoroot
371          */
372         if ( mc->mc_auth_target == META_BOUND_ALL ) {
373                 bound = 1;
374                 goto done;
375         }
376
377         for ( i = 0, msc = &mc->mc_conns[ 0 ]; !META_LAST( msc ); ++i, ++msc ) {
378                 int             rc;
379
380                 /*
381                  * Not a candidate or something wrong with this target ...
382                  */
383                 if ( msc->msc_ld == NULL ) {
384                         continue;
385                 }
386
387                 /*
388                  * If the target is already bound it is skipped
389                  */
390                 if ( msc->msc_bound == META_BOUND && mc->mc_auth_target == i ) {
391                         ++bound;
392                         continue;
393                 }
394
395                 rc = meta_back_single_dobind( op, msc, sendok, META_BIND_NRETRIES );
396                 if ( rc != LDAP_SUCCESS ) {
397                         Debug( LDAP_DEBUG_ANY, "%s meta_back_dobind[%d]: "
398                                         "(anonymous) err=%d\n",
399                                         op->o_log_prefix, i, rc );
400
401                         /*
402                          * null cred bind should always succeed
403                          * as anonymous, so a failure means
404                          * the target is no longer candidate possibly
405                          * due to technical reasons (remote host down?)
406                          * so better clear the handle
407                          */
408                         candidates[ i ] = META_NOT_CANDIDATE;
409 #if 0
410                         ( void )meta_clear_one_candidate( msc );
411 #endif
412                         continue;
413                 } /* else */
414                 
415                 candidates[ i ] = META_CANDIDATE;
416                 msc->msc_bound = META_ANONYMOUS;
417                 ++bound;
418         }
419
420 done:;
421         ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
422
423         return( bound > 0 );
424 }
425
426 /*
427  *
428  */
429 int
430 meta_back_is_valid( struct metaconn *mc, int candidate )
431 {
432         struct metasingleconn   *msc;
433         int                     i;
434
435         assert( mc );
436
437         if ( candidate < 0 ) {
438                 return 0;
439         }
440
441         for ( i = 0, msc = &mc->mc_conns[ 0 ]; !META_LAST( msc ) && i < candidate; 
442                         ++i, ++msc );
443         
444         if ( !META_LAST( msc ) ) {
445                 return ( msc->msc_ld != NULL );
446         }
447
448         return 0;
449 }
450
451 /*
452  * meta_back_rebind
453  *
454  * This is a callback used for chasing referrals using the same
455  * credentials as the original user on this session.
456  */
457 static int 
458 meta_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
459         ber_int_t msgid, void *params )
460 {
461         struct metasingleconn   *msc = (struct metasingleconn *)params;
462
463         return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
464                         LDAP_SASL_SIMPLE, &msc->msc_cred,
465                         NULL, NULL, NULL );
466 }
467
468 /*
469  * FIXME: error return must be handled in a cleaner way ...
470  */
471 int
472 meta_back_op_result(
473         struct metaconn *mc,
474         Operation       *op,
475         SlapReply       *rs,
476         int             candidate )
477 {
478         int                     i,
479                                 rerr = LDAP_SUCCESS;
480         struct metasingleconn   *msc;
481         char                    *rmsg = NULL;
482         char                    *rmatch = NULL;
483         int                     free_rmsg = 0,
484                                 free_rmatch = 0;
485
486         if ( candidate != META_TARGET_NONE ) {
487                 msc = &mc->mc_conns[ candidate ];
488
489                 rs->sr_err = LDAP_SUCCESS;
490
491                 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
492                 if ( rs->sr_err != LDAP_SUCCESS ) {
493                         /*
494                          * better check the type of error. In some cases
495                          * (search ?) it might be better to return a
496                          * success if at least one of the targets gave
497                          * positive result ...
498                          */
499                         ldap_get_option( msc->msc_ld,
500                                         LDAP_OPT_ERROR_STRING, &rmsg );
501                         ldap_get_option( msc->msc_ld,
502                                         LDAP_OPT_MATCHED_DN, &rmatch );
503                         rerr = rs->sr_err = slap_map_api2result( rs );
504
505                         if ( rmsg ) {
506                                 free_rmsg = 1;
507                         }
508                         if ( rmatch ) {
509                                 free_rmatch = 1;
510                         }
511
512                         Debug(LDAP_DEBUG_ANY,
513                                         "==> meta_back_op_result: target"
514                                         " <%d> sending msg \"%s\""
515                                         " (matched \"%s\")\n", 
516                                         candidate, ( rmsg ? rmsg : "" ),
517                                         ( rmatch ? rmatch : "" ) );
518                 }
519
520         } else {
521                 for ( i = 0, msc = &mc->mc_conns[ 0 ]; !META_LAST( msc ); ++i, ++msc ) {
522                         char    *msg = NULL;
523                         char    *match = NULL;
524
525                         rs->sr_err = LDAP_SUCCESS;
526
527                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
528                         if ( rs->sr_err != LDAP_SUCCESS ) {
529                                 /*
530                                  * better check the type of error. In some cases
531                                  * (search ?) it might be better to return a
532                                  * success if at least one of the targets gave
533                                  * positive result ...
534                                  */
535                                 ldap_get_option( msc->msc_ld,
536                                                 LDAP_OPT_ERROR_STRING, &msg );
537                                 ldap_get_option( msc->msc_ld,
538                                                 LDAP_OPT_MATCHED_DN, &match );
539                                 rs->sr_err = slap_map_api2result( rs );
540         
541                                 Debug(LDAP_DEBUG_ANY,
542                                                 "==> meta_back_op_result: target"
543                                                 " <%d> sending msg \"%s\""
544                                                 " (matched \"%s\")\n", 
545                                                 i, ( msg ? msg : "" ),
546                                                 ( match ? match : "" ) );
547         
548                                 /*
549                                  * FIXME: need to rewrite "match" (need rwinfo)
550                                  */
551                                 switch ( rs->sr_err ) {
552                                 default:
553                                         rerr = rs->sr_err;
554                                         if ( rmsg ) {
555                                                 ber_memfree( rmsg );
556                                         }
557                                         rmsg = msg;
558                                         free_rmsg = 1;
559                                         msg = NULL;
560                                         if ( rmatch ) {
561                                                 ber_memfree( rmatch );
562                                         }
563                                         rmatch = match;
564                                         free_rmatch = 1;
565                                         match = NULL;
566                                         break;
567                                 }
568         
569                                 /* better test the pointers before freeing? */
570                                 if ( match ) {
571                                         free( match );
572                                 }
573                                 if ( msg ) {
574                                         free( msg );
575                                 }
576                         }
577                 }
578         }
579         
580         rs->sr_err = rerr;
581         rs->sr_text = rmsg;
582         rs->sr_matched = rmatch;
583         send_ldap_result( op, rs );
584         if ( free_rmsg ) {
585                 ber_memfree( rmsg );
586         }
587         if ( free_rmatch ) {
588                 ber_memfree( rmatch );
589         }
590         rs->sr_text = NULL;
591         rs->sr_matched = NULL;
592
593         return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );
594 }
595