]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/bind.c
91f5be651cd2251f91043af7d62d484c41a420fd
[openldap] / servers / slapd / back-ldap / bind.c
1 /* bind.c - ldap backend bind function */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1999-2003 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by the Howard Chu for inclusion
18  * in OpenLDAP Software and subsequently enhanced by Pierangelo
19  * Masarati.
20  */
21 /* This is an altered version */
22 /*
23  * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
24  * 
25  * Permission is granted to anyone to use this software for any purpose
26  * on any computer system, and to alter it and redistribute it, subject
27  * to the following restrictions:
28  * 
29  * 1. The author is not responsible for the consequences of use of this
30  *    software, no matter how awful, even if they arise from flaws in it.
31  * 
32  * 2. The origin of this software must not be misrepresented, either by
33  *    explicit claim or by omission.  Since few users ever read sources,
34  *    credits should appear in the documentation.
35  * 
36  * 3. Altered versions must be plainly marked as such, and must not be
37  *    misrepresented as being the original software.  Since few users
38  *    ever read sources, credits should appear in the documentation.
39  * 
40  * 4. This notice may not be removed or altered.
41  *
42  *
43  *
44  * Copyright 2000, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
45  * 
46  * This software is being modified by Pierangelo Masarati.
47  * The previously reported conditions apply to the modified code as well.
48  * Changes in the original code are highlighted where required.
49  * Credits for the original code go to the author, Howard Chu.
50  */
51
52 #include "portable.h"
53
54 #include <stdio.h>
55
56 #include <ac/socket.h>
57 #include <ac/string.h>
58
59
60 #define AVL_INTERNAL
61 #include "slap.h"
62 #include "back-ldap.h"
63
64 #define PRINT_CONNTREE 0
65
66 static LDAP_REBIND_PROC ldap_back_rebind;
67
68 int
69 ldap_back_bind(
70     Operation           *op,
71     SlapReply           *rs )
72 {
73         struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
74         struct ldapconn *lc;
75
76         struct berval mdn = { 0, NULL };
77         int rc = 0;
78         ber_int_t msgid;
79         dncookie dc;
80
81         lc = ldap_back_getconn(op, rs);
82         if ( !lc ) {
83                 return( -1 );
84         }
85
86         /*
87          * Rewrite the bind dn if needed
88          */
89         dc.rwmap = &li->rwmap;
90 #ifdef ENABLE_REWRITE
91         dc.conn = op->o_conn;
92         dc.rs = rs;
93         dc.ctx = "bindDn";
94 #else
95         dc.tofrom = 1;
96         dc.normalized = 0;
97 #endif
98         if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
99                 send_ldap_result( op, rs );
100                 return -1;
101         }
102
103         if ( lc->bound_dn.bv_val ) {
104                 ch_free( lc->bound_dn.bv_val );
105                 lc->bound_dn.bv_len = 0;
106                 lc->bound_dn.bv_val = NULL;
107         }
108         lc->bound = 0;
109         /* method is always LDAP_AUTH_SIMPLE if we got here */
110         rs->sr_err = ldap_sasl_bind(lc->ld, mdn.bv_val, LDAP_SASL_SIMPLE,
111                 &op->oq_bind.rb_cred, op->o_ctrls, NULL, &msgid);
112         rc = ldap_back_op_result( lc, op, rs, msgid, 1 );
113         if (rc == LDAP_SUCCESS) {
114                 lc->bound = 1;
115                 if ( mdn.bv_val != op->o_req_dn.bv_val ) {
116                         lc->bound_dn = mdn;
117                 } else {
118                         ber_dupbv( &lc->bound_dn, &op->o_req_dn );
119                 }
120                 mdn.bv_val = NULL;
121
122                 if ( li->savecred ) {
123                         if ( lc->cred.bv_val ) {
124                                 memset( lc->cred.bv_val, 0, lc->cred.bv_len );
125                                 ch_free( lc->cred.bv_val );
126                         }
127                         ber_dupbv( &lc->cred, &op->oq_bind.rb_cred );
128                         ldap_set_rebind_proc( lc->ld, ldap_back_rebind, lc );
129                 }
130         }
131
132         /* must re-insert if local DN changed as result of bind */
133         if ( lc->bound && !bvmatch(&op->o_req_ndn, &lc->local_dn ) ) {
134                 int lerr;
135
136                 ldap_pvt_thread_mutex_lock( &li->conn_mutex );
137                 lc = avl_delete( &li->conntree, (caddr_t)lc,
138                                 ldap_back_conn_cmp );
139                 if ( lc->local_dn.bv_val )
140                         ch_free( lc->local_dn.bv_val );
141                 ber_dupbv( &lc->local_dn, &op->o_req_ndn );
142                 lerr = avl_insert( &li->conntree, (caddr_t)lc,
143                         ldap_back_conn_cmp, ldap_back_conn_dup );
144                 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
145                 if ( lerr == -1 ) {
146                         ldap_back_conn_free( lc );
147                 }
148         }
149
150         if ( mdn.bv_val && mdn.bv_val != op->o_req_dn.bv_val ) {
151                 free( mdn.bv_val );
152         }
153
154         return( rc );
155 }
156
157 /*
158  * ldap_back_conn_cmp
159  *
160  * compares two struct ldapconn based on the value of the conn pointer;
161  * used by avl stuff
162  */
163 int
164 ldap_back_conn_cmp(
165         const void *c1,
166         const void *c2
167         )
168 {
169         const struct ldapconn *lc1 = (const struct ldapconn *)c1;
170         const struct ldapconn *lc2 = (const struct ldapconn *)c2;
171         int rc;
172         
173         /* If local DNs don't match, it is definitely not a match */
174         if ( ( rc = ber_bvcmp( &lc1->local_dn, &lc2->local_dn )) )
175                 return rc;
176
177         /* For shared sessions, conn is NULL. Only explicitly
178          * bound sessions will have non-NULL conn.
179          */
180         return SLAP_PTRCMP(lc1->conn, lc2->conn);
181 }
182
183 /*
184  * ldap_back_conn_dup
185  *
186  * returns -1 in case a duplicate struct ldapconn has been inserted;
187  * used by avl stuff
188  */
189 int
190 ldap_back_conn_dup(
191         void *c1,
192         void *c2
193         )
194 {
195         struct ldapconn *lc1 = (struct ldapconn *)c1;
196         struct ldapconn *lc2 = (struct ldapconn *)c2;
197
198         /* Cannot have more than one shared session with same DN */
199         if ( dn_match( &lc1->local_dn, &lc2->local_dn ) &&
200                  lc1->conn == lc2->conn ) return -1;
201                 
202         return 0;
203 }
204
205 #if PRINT_CONNTREE > 0
206 static void ravl_print( Avlnode *root, int depth )
207 {
208         int     i;
209         struct ldapconn *lc;
210         
211         if ( root == 0 )
212                 return;
213         
214         ravl_print( root->avl_right, depth+1 );
215         
216         for ( i = 0; i < depth; i++ )
217                 printf( "   " );
218
219         lc = root->avl_data;
220         printf( "lc(%lx) local(%s) conn(%lx) %d\n",
221                         lc, lc->local_dn.bv_val, lc->conn, root->avl_bf );
222         
223         ravl_print( root->avl_left, depth+1 );
224 }
225
226 static void myprint( Avlnode *root )
227 {
228         printf( "********\n" );
229         
230         if ( root == 0 )
231                 printf( "\tNULL\n" );
232
233         else
234                 ravl_print( root, 0 );
235         
236         printf( "********\n" );
237 }
238 #endif /* PRINT_CONNTREE */
239
240 struct ldapconn *
241 ldap_back_getconn(Operation *op, SlapReply *rs)
242 {
243         struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
244         struct ldapconn *lc, lc_curr;
245         LDAP *ld;
246         int is_priv = 0;
247
248         /* Searches for a ldapconn in the avl tree */
249
250         /* Explicit binds must not be shared */
251         if ( op->o_tag == LDAP_REQ_BIND
252                 || (op->o_conn
253                   && (op->o_bd == op->o_conn->c_authz_backend ))) {
254                 lc_curr.conn = op->o_conn;
255         } else {
256                 lc_curr.conn = NULL;
257         }
258         
259         /* Internal searches are privileged and shared. So is root. */
260         if ( op->o_do_not_cache || be_isroot( li->be, &op->o_ndn ) ) {
261                 lc_curr.local_dn = li->be->be_rootndn;
262                 lc_curr.conn = NULL;
263                 is_priv = 1;
264         } else {
265                 lc_curr.local_dn = op->o_ndn;
266         }
267
268         ldap_pvt_thread_mutex_lock( &li->conn_mutex );
269         lc = (struct ldapconn *)avl_find( li->conntree, 
270                 (caddr_t)&lc_curr, ldap_back_conn_cmp );
271         ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
272
273         /* Looks like we didn't get a bind. Open a new session... */
274         if (!lc) {
275                 int vers = op->o_protocol;
276                 rs->sr_err = ldap_initialize(&ld, li->url);
277                 
278                 if (rs->sr_err != LDAP_SUCCESS) {
279                         rs->sr_err = ldap_back_map_result(rs);
280                         if (rs->sr_text == NULL) {
281                                 rs->sr_text = "ldap_initialize() failed";
282                         }
283                         if (op->o_conn) send_ldap_result( op, rs );
284                         rs->sr_text = NULL;
285                         return( NULL );
286                 }
287                 /* Set LDAP version. This will always succeed: If the client
288                  * bound with a particular version, then so can we.
289                  */
290                 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
291                                 (const void *)&vers);
292                 /* FIXME: configurable? */
293                 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
294
295                 lc = (struct ldapconn *)ch_malloc(sizeof(struct ldapconn));
296                 lc->conn = lc_curr.conn;
297                 lc->ld = ld;
298                 ber_dupbv( &lc->local_dn, &lc_curr.local_dn );
299
300 #ifdef ENABLE_REWRITE
301                 /*
302                  * Sets a cookie for the rewrite session
303                  *
304                  * FIXME: the o_conn might be no longer valid,
305                  * since we may have different entries
306                  * for the same connection
307                  */
308                 ( void )rewrite_session_init( li->rwmap.rwm_rw, op->o_conn );
309 #endif /* ENABLE_REWRITE */
310
311                 ldap_pvt_thread_mutex_init( &lc->lc_mutex );
312
313                 if ( is_priv ) {
314                         ber_dupbv( &lc->cred, &li->bindpw );
315                         ber_dupbv( &lc->bound_dn, &li->binddn );
316                 } else {
317                         lc->cred.bv_len = 0;
318                         lc->cred.bv_val = NULL;
319                         lc->bound_dn.bv_val = NULL;
320                         lc->bound_dn.bv_len = 0;
321                         if ( op->o_conn && op->o_conn->c_dn.bv_len != 0
322                                         && ( op->o_bd == op->o_conn->c_authz_backend ) ) {
323                                 
324                                 dncookie dc;
325                                 struct berval bv;
326
327                                 /*
328                                  * Rewrite the bind dn if needed
329                                  */
330                                 dc.rwmap = &li->rwmap;
331 #ifdef ENABLE_REWRITE
332                                 dc.conn = op->o_conn;
333                                 dc.rs = rs;
334                                 dc.ctx = "bindDn";
335 #else
336                                 dc.tofrom = 1;
337                                 dc.normalized = 0;
338 #endif
339
340                                 if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn, &bv ) ) {
341                                         send_ldap_result( op, rs );
342                                         return NULL;
343                                 }
344
345                                 if ( bv.bv_val == op->o_conn->c_dn.bv_val ) {
346                                         ber_dupbv( &lc->bound_dn, &bv );
347                                 } else {
348                                         lc->bound_dn = bv;
349                                 }
350                         }
351                 }
352
353                 lc->bound = 0;
354
355                 /* Inserts the newly created ldapconn in the avl tree */
356                 ldap_pvt_thread_mutex_lock( &li->conn_mutex );
357                 rs->sr_err = avl_insert( &li->conntree, (caddr_t)lc,
358                         ldap_back_conn_cmp, ldap_back_conn_dup );
359
360 #if PRINT_CONNTREE > 0
361                 myprint( li->conntree );
362 #endif /* PRINT_CONNTREE */
363         
364                 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
365
366 #ifdef NEW_LOGGING
367                 LDAP_LOG( BACK_LDAP, INFO, 
368                         "ldap_back_getconn: conn %p inserted\n", (void *) lc, 0, 0);
369 #else /* !NEW_LOGGING */
370                 Debug( LDAP_DEBUG_TRACE,
371                         "=>ldap_back_getconn: conn %p inserted\n", (void *) lc, 0, 0 );
372 #endif /* !NEW_LOGGING */
373         
374                 /* Err could be -1 in case a duplicate ldapconn is inserted */
375                 if ( rs->sr_err != 0 ) {
376                         ldap_back_conn_free( lc );
377                         if (op->o_conn) {
378                                 send_ldap_error( op, rs, LDAP_OTHER,
379                                 "internal server error" );
380                         }
381                         return( NULL );
382                 }
383         } else {
384 #ifdef NEW_LOGGING
385                 LDAP_LOG( BACK_LDAP, INFO, 
386                         "ldap_back_getconn: conn %p fetched\n", 
387                         (void *) lc, 0, 0 );
388 #else /* !NEW_LOGGING */
389                 Debug( LDAP_DEBUG_TRACE,
390                         "=>ldap_back_getconn: conn %p fetched\n", (void *) lc, 0, 0 );
391 #endif /* !NEW_LOGGING */
392         }
393         
394         return( lc );
395 }
396
397 /*
398  * ldap_back_dobind
399  *
400  * Note: as the check for the value of lc->bound was already here, I removed
401  * it from all the callers, and I made the function return the flag, so
402  * it can be used to simplify the check.
403  */
404 int
405 ldap_back_dobind( struct ldapconn *lc, Operation *op, SlapReply *rs )
406 {       
407         int rc;
408         ber_int_t msgid;
409
410         ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
411         if ( !lc->bound ) {
412                 rs->sr_err = ldap_sasl_bind(lc->ld, lc->bound_dn.bv_val,
413                         LDAP_SASL_SIMPLE, &lc->cred, NULL, NULL, &msgid);
414                 rc = ldap_back_op_result( lc, op, rs, msgid, 0 );
415                 if (rc == LDAP_SUCCESS) {
416                         lc->bound = 1;
417                 }
418         }
419         rc = lc->bound;
420         ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
421         return rc;
422 }
423
424 /*
425  * ldap_back_rebind
426  *
427  * This is a callback used for chasing referrals using the same
428  * credentials as the original user on this session.
429  */
430 static int 
431 ldap_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
432         ber_int_t msgid, void *params )
433 {
434         struct ldapconn *lc = params;
435
436         return ldap_bind_s( ld, lc->bound_dn.bv_val, lc->cred.bv_val, LDAP_AUTH_SIMPLE );
437 }
438
439 /* Map API errors to protocol errors... */
440
441 int
442 ldap_back_map_result(SlapReply *rs)
443 {
444         switch(rs->sr_err)
445         {
446         case LDAP_SERVER_DOWN:
447                 return LDAP_UNAVAILABLE;
448         case LDAP_LOCAL_ERROR:
449                 return LDAP_OTHER;
450         case LDAP_ENCODING_ERROR:
451         case LDAP_DECODING_ERROR:
452                 return LDAP_PROTOCOL_ERROR;
453         case LDAP_TIMEOUT:
454                 return LDAP_UNAVAILABLE;
455         case LDAP_AUTH_UNKNOWN:
456                 return LDAP_AUTH_METHOD_NOT_SUPPORTED;
457         case LDAP_FILTER_ERROR:
458                 rs->sr_text = "Filter error";
459                 return LDAP_OTHER;
460         case LDAP_USER_CANCELLED:
461                 rs->sr_text = "User cancelled";
462                 return LDAP_OTHER;
463         case LDAP_PARAM_ERROR:
464                 return LDAP_PROTOCOL_ERROR;
465         case LDAP_NO_MEMORY:
466                 return LDAP_OTHER;
467         case LDAP_CONNECT_ERROR:
468                 return LDAP_UNAVAILABLE;
469         case LDAP_NOT_SUPPORTED:
470                 return LDAP_UNWILLING_TO_PERFORM;
471         case LDAP_CONTROL_NOT_FOUND:
472                 return LDAP_PROTOCOL_ERROR;
473         case LDAP_NO_RESULTS_RETURNED:
474                 return LDAP_NO_SUCH_OBJECT;
475         case LDAP_MORE_RESULTS_TO_RETURN:
476                 rs->sr_text = "More results to return";
477                 return LDAP_OTHER;
478         case LDAP_CLIENT_LOOP:
479         case LDAP_REFERRAL_LIMIT_EXCEEDED:
480                 return LDAP_LOOP_DETECT;
481         default:
482                 if LDAP_API_ERROR(rs->sr_err)
483                         return LDAP_OTHER;
484                 else
485                         return rs->sr_err;
486         }
487 }
488
489 int
490 ldap_back_op_result(struct ldapconn *lc, Operation *op, SlapReply *rs,
491         ber_int_t msgid, int sendok)
492 {
493         struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
494         char *match = NULL;
495         LDAPMessage *res;
496         char *text = NULL;
497
498         rs->sr_text = NULL;
499         rs->sr_matched = NULL;
500
501         if (rs->sr_err == LDAP_SUCCESS) {
502                 if (ldap_result(lc->ld, msgid, 1, NULL, &res) == -1) {
503                         ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
504                                         &rs->sr_err);
505                 } else {
506                         int rc = ldap_parse_result(lc->ld, res, &rs->sr_err,
507                                         &match, &text, NULL, NULL, 1);
508                         rs->sr_text = text;
509                         if (rc != LDAP_SUCCESS) rs->sr_err = rc;
510                 }
511         }
512
513         if (rs->sr_err != LDAP_SUCCESS) {
514                 rs->sr_err = ldap_back_map_result(rs);
515
516                 /* internal ops must not reply to client */
517                 if ( op->o_conn && !op->o_do_not_cache && match ) {
518                         struct berval dn, mdn;
519                         dncookie dc;
520
521                         dc.rwmap = &li->rwmap;
522 #ifdef ENABLE_REWRITE
523                         dc.conn = op->o_conn;
524                         dc.rs = rs;
525                         dc.ctx = "matchedDn";
526 #else
527                         dc.tofrom = 0;
528                         dc.normalized = 0;
529 #endif
530                         ber_str2bv(match, 0, 0, &dn);
531                         ldap_back_dn_massage(&dc, &dn, &mdn);
532                         rs->sr_matched = mdn.bv_val;
533                                 
534                 }
535         }
536         if (op->o_conn && (sendok || rs->sr_err != LDAP_SUCCESS)) {
537                 send_ldap_result( op, rs );
538         }
539         if ( match ) {
540                 if ( rs->sr_matched != match ) {
541                         free( (char *)rs->sr_matched );
542                 }
543                 rs->sr_matched = NULL;
544                 ldap_memfree( match );
545         }
546         if ( text ) {
547                 ldap_memfree( text );
548         }
549         rs->sr_text = NULL;
550         return( (rs->sr_err == LDAP_SUCCESS) ? 0 : -1 );
551 }
552