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