]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/bind.c
eec3d54b370f207ec615a4c4dc6af7245c998181
[openldap] / servers / slapd / back-meta / bind.c
1 /*
2  * Copyright 1998-2003 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 LDAP_REBIND_PROC meta_back_rebind;
81
82 static int
83 meta_back_do_single_bind(
84                 struct metaconn         *lc,
85                 Operation               *op,
86                 int                     candidate
87 );
88
89 int
90 meta_back_bind( Operation *op, SlapReply *rs )
91 {
92         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
93         struct metaconn *lc;
94
95         int rc = -1, i, gotit = 0, ndnlen, isroot = 0;
96         int op_type = META_OP_ALLOW_MULTIPLE;
97         int err = LDAP_SUCCESS;
98
99 #ifdef NEW_LOGGING
100         LDAP_LOG( BACK_META, ENTRY, "meta_back_bind: dn: %s.\n",
101                         op->o_req_dn.bv_val, 0, 0 );
102 #else /* !NEW_LOGGING */
103         Debug( LDAP_DEBUG_ARGS, "meta_back_bind: dn: %s.\n%s%s",
104                         op->o_req_dn.bv_val, "", "" );
105 #endif /* !NEW_LOGGING */
106
107         if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
108                 isroot = 1;
109                 ber_dupbv( &op->oq_bind.rb_edn, be_root_dn( op->o_bd ) );
110                 op_type = META_OP_REQUIRE_ALL;
111         }
112         lc = meta_back_getconn( op, rs, op_type,
113                         &op->o_req_ndn, NULL );
114         if ( !lc ) {
115 #ifdef NEW_LOGGING
116                 LDAP_LOG( BACK_META, NOTICE,
117                                 "meta_back_bind: no target for dn %s.\n", dn->bv_val, 0, 0 );
118 #else /* !NEW_LOGGING */
119                 Debug( LDAP_DEBUG_ANY,
120                                 "meta_back_bind: no target for dn %s.\n%s%s",
121                                 op->o_req_dn.bv_val, "", "");
122 #endif /* !NEW_LOGGING */
123
124                 send_ldap_result( op, rs );
125                 return -1;
126         }
127
128         /*
129          * Each target is scanned ...
130          */
131         lc->bound_target = META_BOUND_NONE;
132         ndnlen = op->o_req_ndn.bv_len;
133         for ( i = 0; i < li->ntargets; i++ ) {
134                 int             lerr;
135                 struct berval   orig_dn = op->o_req_dn;
136                 struct berval   orig_ndn = op->o_req_ndn;
137                 struct berval   orig_cred = op->oq_bind.rb_cred;
138                 int             orig_method = op->oq_bind.rb_method;
139                 
140
141                 /*
142                  * Skip non-candidates
143                  */
144                 if ( lc->conns[ i ].candidate != META_CANDIDATE ) {
145                         continue;
146                 }
147
148                 if ( gotit == 0 ) {
149                         gotit = 1;
150                 } else {
151                         /*
152                          * A bind operation is expected to have
153                          * ONE CANDIDATE ONLY!
154                          */
155 #ifdef NEW_LOGGING
156                         LDAP_LOG( BACK_META, WARNING,
157                                         "==>meta_back_bind: more than one"
158                                         " candidate is attempting to bind"
159                                         " ...\n" , 0, 0, 0 );
160 #else /* !NEW_LOGGING */
161                         Debug( LDAP_DEBUG_ANY,
162                                         "==>meta_back_bind: more than one"
163                                         " candidate is attempting to bind"
164                                         " ...\n%s%s%s", 
165                                         "", "", "" );
166 #endif /* !NEW_LOGGING */
167                 }
168
169                 if ( isroot && li->targets[ i ]->pseudorootdn.bv_val != NULL ) {
170                         op->o_req_dn = li->targets[ i ]->pseudorootdn;
171                         op->o_req_ndn = li->targets[ i ]->pseudorootdn;
172                         op->oq_bind.rb_cred = li->targets[ i ]->pseudorootpw;
173                         op->oq_bind.rb_method = LDAP_AUTH_SIMPLE;
174                 }
175                 
176                 lerr = meta_back_do_single_bind( lc, op, i );
177                 if ( lerr != LDAP_SUCCESS ) {
178                         err = lerr;
179                         ( void )meta_clear_one_candidate( &lc->conns[ i ], 1 );
180                 } else {
181                         rc = LDAP_SUCCESS;
182                 }
183
184                 op->o_req_dn = orig_dn;
185                 op->o_req_ndn = orig_ndn;
186                 op->oq_bind.rb_cred = orig_cred;
187                 op->oq_bind.rb_method = orig_method;
188         }
189
190         if ( isroot ) {
191                 lc->bound_target = META_BOUND_ALL;
192         }
193
194         /*
195          * rc is LDAP_SUCCESS if at least one bind succeeded,
196          * err is the last error that occurred during a bind;
197          * if at least (and at most?) one bind succeedes, fine.
198          */
199         if ( rc != LDAP_SUCCESS /* && err != LDAP_SUCCESS */ ) {
200                 
201                 /*
202                  * deal with bind failure ...
203                  */
204
205                 /*
206                  * no target was found within the naming context, 
207                  * so bind must fail with invalid credentials
208                  */
209                 if ( err == LDAP_SUCCESS && gotit == 0 ) {
210                         err = LDAP_INVALID_CREDENTIALS;
211                 }
212
213                 rs->sr_err = ldap_back_map_result( err );
214                 send_ldap_result( op, rs );
215                 return -1;
216         }
217
218         return 0;
219 }
220
221 /*
222  * meta_back_do_single_bind
223  *
224  * attempts to perform a bind with creds
225  */
226 static int
227 meta_back_do_single_bind(
228                 struct metaconn         *lc,
229                 Operation               *op,
230                 int                     candidate
231 )
232 {
233         struct metainfo *li = ( struct metainfo * )op->o_bd->be_private;
234         struct berval   mdn = { 0, NULL };
235         int             rc;
236         ber_int_t       msgid;
237         
238         /*
239          * Rewrite the bind dn if needed
240          */
241         switch ( rewrite_session( li->targets[ candidate ]->rwinfo,
242                                 "bindDn", op->o_req_dn.bv_val,
243                                 lc->conn, &mdn.bv_val ) ) {
244         case REWRITE_REGEXEC_OK:
245                 if ( mdn.bv_val == NULL ) {
246                         mdn = op->o_req_dn;
247                 } else {
248                         mdn.bv_len = strlen( mdn.bv_val );
249                 }
250 #ifdef NEW_LOGGING
251                 LDAP_LOG( BACK_META, DETAIL1,
252                                 "[rw] bindDn: \"%s\" -> \"%s\"\n",
253                                 op->o_req_dn.bv_val, mdn.bv_val, 0 );
254 #else /* !NEW_LOGGING */
255                 Debug( LDAP_DEBUG_ARGS,
256                                 "rw> bindDn: \"%s\" -> \"%s\"\n%s",
257                                 op->o_req_dn.bv_val, mdn.bv_val, "" );
258 #endif /* !NEW_LOGGING */
259                 break;
260                 
261         case REWRITE_REGEXEC_UNWILLING:
262                 return LDAP_UNWILLING_TO_PERFORM;
263
264         case REWRITE_REGEXEC_ERR:
265                 return LDAP_OTHER;
266         }
267
268         if ( op->o_ctrls ) {
269                 rc = ldap_set_option( lc->conns[ candidate ].ld, 
270                                 LDAP_OPT_SERVER_CONTROLS, op->o_ctrls );
271                 if ( rc != LDAP_SUCCESS ) {
272                         rc = ldap_back_map_result( rc );
273                         goto return_results;
274                 }
275         }
276         
277         rc = ldap_sasl_bind(lc->conns[ candidate ].ld, mdn.bv_val,
278                         LDAP_SASL_SIMPLE, &op->oq_bind.rb_cred,
279                         op->o_ctrls, NULL, &msgid);
280         if ( rc != LDAP_SUCCESS ) {
281                 rc = ldap_back_map_result( rc );
282
283         } else {
284                 /*
285                  * FIXME: handle response!!!
286                  */
287                 ber_dupbv( &lc->conns[ candidate ].bound_dn, &op->o_req_dn );
288                 lc->conns[ candidate ].bound = META_BOUND;
289                 lc->bound_target = candidate;
290
291                 if ( li->savecred ) {
292                         if ( lc->conns[ candidate ].cred.bv_val )
293                                 ch_free( lc->conns[ candidate ].cred.bv_val );
294                         ber_dupbv( &lc->conns[ candidate ].cred,
295                                         &op->oq_bind.rb_cred );
296                         ldap_set_rebind_proc( lc->conns[ candidate ].ld, 
297                                         meta_back_rebind, 
298                                         &lc->conns[ candidate ] );
299                 }
300
301                 if ( li->cache.ttl != META_DNCACHE_DISABLED
302                                 && op->o_req_ndn.bv_len != 0 ) {
303                         ( void )meta_dncache_update_entry( &li->cache,
304                                         &op->o_req_ndn, candidate );
305                 }
306         }
307
308 return_results:;
309         
310         if ( mdn.bv_val != op->o_req_dn.bv_val ) {
311                 free( mdn.bv_val );
312         }
313
314         return rc;
315 }
316
317 /*
318  * meta_back_dobind
319  */
320 int
321 meta_back_dobind( struct metaconn *lc, Operation *op )
322 {
323         struct metasingleconn *lsc;
324         int bound = 0, i;
325
326         /*
327          * all the targets are bound as pseudoroot
328          */
329         if ( lc->bound_target == META_BOUND_ALL ) {
330                 return 1;
331         }
332
333         for ( i = 0, lsc = lc->conns; !META_LAST(lsc); ++i, ++lsc ) {
334                 int rc;
335
336                 /*
337                  * Not a candidate or something wrong with this target ...
338                  */
339                 if ( lsc->ld == NULL ) {
340                         continue;
341                 }
342
343                 /*
344                  * If required, set controls
345                  */
346                 if ( op->o_ctrls ) {
347                         if ( ldap_set_option( lsc->ld, LDAP_OPT_SERVER_CONTROLS,
348                                         op->o_ctrls ) != LDAP_SUCCESS ) {
349                                 ( void )meta_clear_one_candidate( lsc, 1 );
350                                 continue;
351                         }
352                 }
353         
354                 /*
355                  * If the target is already bound it is skipped
356                  */
357                 if ( lsc->bound == META_BOUND && lc->bound_target == i ) {
358                         ++bound;
359                         continue;
360                 }
361
362                 /*
363                  * Otherwise an anonymous bind is performed
364                  * (note: if the target was already bound, the anonymous
365                  * bind clears the previous bind).
366                  */
367                 if ( lsc->bound_dn.bv_val ) {
368                         ch_free( lsc->bound_dn.bv_val );
369                         lsc->bound_dn.bv_val = NULL;
370                         lsc->bound_dn.bv_len = 0;
371                 }
372                 
373
374                 rc = ldap_bind_s( lsc->ld, 0, NULL, LDAP_AUTH_SIMPLE );
375                 if ( rc != LDAP_SUCCESS ) {
376                         
377 #ifdef NEW_LOGGING
378                         LDAP_LOG( BACK_META, WARNING,
379                                         "meta_back_dobind: (anonymous)"
380                                         " bind failed"
381                                         " with error %d (%s)\n",
382                                         rc, ldap_err2string( rc ), 0 );
383 #else /* !NEW_LOGGING */
384                         Debug( LDAP_DEBUG_ANY,
385                                         "==>meta_back_dobind: (anonymous)"
386                                         " bind failed"
387                                         " with error %d (%s)\n",
388                                         rc, ldap_err2string( rc ), 0 );
389 #endif /* !NEW_LOGGING */
390
391                         /*
392                          * null cred bind should always succeed
393                          * as anonymous, so a failure means
394                          * the target is no longer candidate possibly
395                          * due to technical reasons (remote host down?)
396                          * so better clear the handle
397                          */
398                         ( void )meta_clear_one_candidate( lsc, 1 );
399                         continue;
400                 } /* else */
401                 
402                 lsc->bound = META_ANONYMOUS;
403                 ++bound;
404         }
405
406         return( bound > 0 );
407 }
408
409 /*
410  *
411  */
412 int
413 meta_back_is_valid( struct metaconn *lc, int candidate )
414 {
415         struct metasingleconn   *lsc;
416         int                     i;
417
418         assert( lc );
419
420         if ( candidate < 0 ) {
421                 return 0;
422         }
423
424         for ( i = 0, lsc = lc->conns; !META_LAST(lsc) && i < candidate; 
425                         ++i, ++lsc );
426         
427         if ( !META_LAST(lsc) ) {
428                 return( lsc->ld != NULL );
429         }
430
431         return 0;
432 }
433
434 /*
435  * meta_back_rebind
436  *
437  * This is a callback used for chasing referrals using the same
438  * credentials as the original user on this session.
439  */
440 static int 
441 meta_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
442         ber_int_t msgid, void *params )
443 {
444         struct metasingleconn *lc = params;
445
446         return ldap_bind_s( ld, lc->bound_dn.bv_val, lc->cred.bv_val,
447                         LDAP_AUTH_SIMPLE );
448 }
449
450 /*
451  * FIXME: error return must be handled in a cleaner way ...
452  */
453 int
454 meta_back_op_result( struct metaconn *lc, Operation *op, SlapReply *rs )
455 {
456         int i, rerr = LDAP_SUCCESS;
457         struct metasingleconn *lsc;
458         char *rmsg = NULL;
459         char *rmatch = NULL;
460
461         for ( i = 0, lsc = lc->conns; !META_LAST(lsc); ++i, ++lsc ) {
462                 int err = LDAP_SUCCESS;
463                 char *msg = NULL;
464                 char *match = NULL;
465
466                 ldap_get_option( lsc->ld, LDAP_OPT_ERROR_NUMBER, &err );
467                 if ( err != LDAP_SUCCESS ) {
468                         /*
469                          * better check the type of error. In some cases
470                          * (search ?) it might be better to return a
471                          * success if at least one of the targets gave
472                          * positive result ...
473                          */
474                         ldap_get_option( lsc->ld,
475                                         LDAP_OPT_ERROR_STRING, &msg );
476                         ldap_get_option( lsc->ld,
477                                         LDAP_OPT_MATCHED_DN, &match );
478                         err = ldap_back_map_result( err );
479
480 #ifdef NEW_LOGGING
481                         LDAP_LOG( BACK_META, RESULTS,
482                                         "meta_back_op_result: target"
483                                         " <%d> sending msg \"%s\""
484                                         " (matched \"%s\")\n",
485                                         i, ( msg ? msg : "" ),
486                                         ( match ? match : "" ) );
487 #else /* !NEW_LOGGING */
488                         Debug(LDAP_DEBUG_ANY,
489                                         "==> meta_back_op_result: target"
490                                         " <%d> sending msg \"%s\""
491                                         " (matched \"%s\")\n", 
492                                         i, ( msg ? msg : "" ),
493                                         ( match ? match : "" ) );
494 #endif /* !NEW_LOGGING */
495
496                         /*
497                          * FIXME: need to rewrite "match" (need rwinfo)
498                          */
499                         switch ( err ) {
500                         default:
501                                 rerr = err;
502                                 rmsg = msg;
503                                 msg = NULL;
504                                 rmatch = match;
505                                 match = NULL;
506                                 break;
507                         }
508
509                         /* better test the pointers before freeing? */
510                         if ( match ) {
511                                 free( match );
512                         }
513                         if ( msg ) {
514                                 free( msg );
515                         }
516                 }
517         }
518
519         rs->sr_err = rerr;
520         rs->sr_text = rmsg;
521         rs->sr_matched = rmatch;
522         send_ldap_result( op, rs );
523         rs->sr_text = NULL;
524         rs->sr_matched = NULL;
525
526         return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );
527 }
528