]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/bind.c
Added passwd_exop, added matchedDN rewrite for results.
[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         ber_int_t msgid;
72
73         lc = ldap_back_getconn(li, conn, op);
74         if ( !lc ) {
75                 return( -1 );
76         }
77
78         /*
79          * Rewrite the bind dn if needed
80          */
81 #ifdef ENABLE_REWRITE
82         switch ( rewrite_session( li->rwinfo, "bindDn", dn->bv_val, conn, &mdn.bv_val ) ) {
83         case REWRITE_REGEXEC_OK:
84                 if ( mdn.bv_val == NULL ) {
85                         mdn.bv_val = ( char * )dn->bv_val;
86                 }
87 #ifdef NEW_LOGGING
88                 LDAP_LOG( BACK_LDAP, DETAIL1, 
89                         "[rw] bindDn: \"%s\" -> \"%s\"\n", dn->bv_val, mdn.bv_val, 0 );
90 #else /* !NEW_LOGGING */
91                 Debug( LDAP_DEBUG_ARGS, "rw> bindDn: \"%s\" -> \"%s\"\n%s",
92                                 dn->bv_val, mdn.bv_val, "" );
93 #endif /* !NEW_LOGGING */
94                 break;
95                 
96         case REWRITE_REGEXEC_UNWILLING:
97                 send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM,
98                                 NULL, "Operation not allowed", NULL, NULL );
99                 return( -1 );
100
101         case REWRITE_REGEXEC_ERR:
102                 send_ldap_result( conn, op, LDAP_OTHER,
103                                 NULL, "Rewrite error", NULL, NULL );
104                 return( -1 );
105         }
106 #else /* !ENABLE_REWRITE */
107         ldap_back_dn_massage( li, dn, &mdn, 0, 1 );
108 #endif /* !ENABLE_REWRITE */
109
110         if ( lc->bound_dn.bv_val ) {
111                 ch_free( lc->bound_dn.bv_val );
112                 lc->bound_dn.bv_len = 0;
113                 lc->bound_dn.bv_val = NULL;
114         }
115         lc->bound = 0;
116         /* method is always LDAP_AUTH_SIMPLE if we got here */
117         rc = ldap_sasl_bind(lc->ld, mdn.bv_val, LDAP_SASL_SIMPLE,
118                 cred, op->o_ctrls, NULL, &msgid);
119         rc = ldap_back_op_result( li, lc, conn, op, msgid, rc );
120         if (rc == LDAP_SUCCESS) {
121                 lc->bound = 1;
122                 if ( mdn.bv_val != dn->bv_val ) {
123                         lc->bound_dn = mdn;
124                 } else {
125                         ber_dupbv( &lc->bound_dn, dn );
126                 }
127                 if ( li->savecred ) {
128                         if ( lc->cred.bv_val )
129                                 ch_free( lc->cred.bv_val );
130                         ber_dupbv( &lc->cred, cred );
131                         ldap_set_rebind_proc( lc->ld, ldap_back_rebind, lc );
132                 }
133         }
134
135         /* must re-insert if local DN changed as result of bind */
136         if ( lc->bound && ber_bvcmp(ndn, &lc->local_dn ) ) {
137                 int err;
138                 ldap_pvt_thread_mutex_lock( &li->conn_mutex );
139                 lc = avl_delete( &li->conntree, (caddr_t)lc, ldap_back_conn_cmp );
140                 if ( lc->local_dn.bv_val )
141                         ch_free( lc->local_dn.bv_val );
142                 ber_dupbv( &lc->local_dn, ndn );
143                 err = avl_insert( &li->conntree, (caddr_t)lc,
144                         ldap_back_conn_cmp, ldap_back_conn_dup );
145                 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
146                 if ( err == -1 ) {
147                         ldap_back_conn_free( lc );
148                 }
149         }
150
151         return( rc );
152 }
153
154 /*
155  * ldap_back_conn_cmp
156  *
157  * compares two struct ldapconn based on the value of the conn pointer;
158  * used by avl stuff
159  */
160 int
161 ldap_back_conn_cmp(
162         const void *c1,
163         const void *c2
164         )
165 {
166         const struct ldapconn *lc1 = (const struct ldapconn *)c1;
167         const struct ldapconn *lc2 = (const struct ldapconn *)c2;
168         int rc;
169         
170         /* If local DNs don't match, it is definitely not a match */
171         if ( ( rc = ber_bvcmp( &lc1->local_dn, &lc2->local_dn )) )
172                 return rc;
173
174         /* For shared sessions, conn is NULL. Only explicitly
175          * bound sessions will have non-NULL conn.
176          */
177         return lc1->conn - lc2->conn;
178 }
179
180 /*
181  * ldap_back_conn_dup
182  *
183  * returns -1 in case a duplicate struct ldapconn has been inserted;
184  * used by avl stuff
185  */
186 int
187 ldap_back_conn_dup(
188         void *c1,
189         void *c2
190         )
191 {
192         struct ldapconn *lc1 = (struct ldapconn *)c1;
193         struct ldapconn *lc2 = (struct ldapconn *)c2;
194
195         /* Cannot have more than one shared session with same DN */
196         if ( dn_match( &lc1->local_dn, &lc2->local_dn ) &&
197                  lc1->conn == lc2->conn ) return -1;
198                 
199         return 0;
200 }
201
202 #if PRINT_CONNTREE > 0
203 static void ravl_print( Avlnode *root, int depth )
204 {
205         int     i;
206         struct ldapconn *lc;
207         
208         if ( root == 0 )
209                 return;
210         
211         ravl_print( root->avl_right, depth+1 );
212         
213         for ( i = 0; i < depth; i++ )
214                 printf( "   " );
215
216         lc = root->avl_data;
217         printf( "lc(%lx) local(%s) conn(%lx) %d\n", lc, lc->local_dn.bv_val, lc->conn, root->avl_bf );
218         
219         ravl_print( root->avl_left, depth+1 );
220 }
221
222 static void myprint( Avlnode *root )
223 {
224         printf( "********\n" );
225         
226         if ( root == 0 )
227                 printf( "\tNULL\n" );
228
229         else
230                 ravl_print( root, 0 );
231         
232         printf( "********\n" );
233 }
234 #endif /* PRINT_CONNTREE */
235
236 struct ldapconn *
237 ldap_back_getconn(struct ldapinfo *li, Connection *conn, Operation *op)
238 {
239         struct ldapconn *lc, lc_curr;
240         LDAP *ld;
241         int is_priv = 0;
242
243         /* Searches for a ldapconn in the avl tree */
244
245         /* Explicit binds must not be shared */
246         if ( op->o_tag == LDAP_REQ_BIND ) {
247                 lc_curr.conn = conn;
248         } else {
249                 lc_curr.conn = NULL;
250         }
251         
252         /* Internal searches are privileged. So is root. */
253         if ( op->o_do_not_cache || be_isroot( li->be, &op->o_ndn ) ) {
254                 lc_curr.local_dn = li->be->be_rootndn;
255                 is_priv = 1;
256         } else {
257                 lc_curr.local_dn = op->o_ndn;
258         }
259         ldap_pvt_thread_mutex_lock( &li->conn_mutex );
260         lc = (struct ldapconn *)avl_find( li->conntree, 
261                 (caddr_t)&lc_curr, ldap_back_conn_cmp );
262         ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
263
264         /* Looks like we didn't get a bind. Open a new session... */
265         if (!lc) {
266                 int vers = conn->c_protocol;
267                 int err = ldap_initialize(&ld, li->url);
268                 
269                 if (err != LDAP_SUCCESS) {
270                         err = ldap_back_map_result(err);
271                         send_ldap_result( conn, op, err,
272                                 NULL, "ldap_initialize() failed", NULL, NULL );
273                         return( NULL );
274                 }
275                 /* Set LDAP version. This will always succeed: If the client
276                  * bound with a particular version, then so can we.
277                  */
278                 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &vers);
279
280                 lc = (struct ldapconn *)ch_malloc(sizeof(struct ldapconn));
281                 lc->conn = lc_curr.conn;
282                 lc->ld = ld;
283                 ber_dupbv( &lc->local_dn, &lc_curr.local_dn );
284
285                 if ( is_priv ) {
286                         ber_str2bv( li->bindpw, 0, 1, &lc->cred );
287                 } else {
288                         lc->cred.bv_len = 0;
289                         lc->cred.bv_val = NULL;
290                 }
291
292                 ldap_pvt_thread_mutex_init( &lc->lc_mutex );
293
294 #ifdef ENABLE_REWRITE
295                 /*
296                  * Sets a cookie for the rewrite session
297                  */
298                 ( void )rewrite_session_init( li->rwinfo, conn );
299 #endif /* ENABLE_REWRITE */
300
301                 if ( conn->c_dn.bv_len != 0 ) {
302                         
303                         /*
304                          * Rewrite the bind dn if needed
305                          */
306 #ifdef ENABLE_REWRITE                   
307                         lc->bound_dn.bv_val = NULL;
308                         lc->bound_dn.bv_len = 0;
309                         switch ( rewrite_session( li->rwinfo, "bindDn",
310                                                 conn->c_dn.bv_val, conn,
311                                                 &lc->bound_dn.bv_val ) ) {
312                         case REWRITE_REGEXEC_OK:
313                                 if ( lc->bound_dn.bv_val == NULL ) {
314                                         ber_dupbv( &lc->bound_dn,
315                                                         &conn->c_dn );
316                                 }
317 #ifdef NEW_LOGGING
318                                 LDAP_LOG( BACK_LDAP, DETAIL1, 
319                                                 "[rw] bindDn: \"%s\" ->" 
320                                                 " \"%s\"\n%s",
321                                                 conn->c_dn.bv_val, 
322                                                 lc->bound_dn.bv_val, "" );
323 #else /* !NEW_LOGGING */
324                                 Debug( LDAP_DEBUG_ARGS,
325                                                 "rw> bindDn: \"%s\" ->"
326                                                 " \"%s\"\n%s",
327                                                 conn->c_dn.bv_val,
328                                                 lc->bound_dn.bv_val, "" );
329 #endif /* !NEW_LOGGING */
330                                 break;
331                                 
332                         case REWRITE_REGEXEC_UNWILLING:
333                                 send_ldap_result( conn, op,
334                                                 LDAP_UNWILLING_TO_PERFORM,
335                                                 NULL, "Operation not allowed",
336                                                 NULL, NULL );
337                                 return( NULL );
338                                 
339                         case REWRITE_REGEXEC_ERR:
340                                 send_ldap_result( conn, op,
341                                                 LDAP_OTHER,
342                                                 NULL, "Rewrite error",
343                                                 NULL, NULL );
344                                 return( NULL );
345                         }
346
347 #else /* !ENABLE_REWRITE */
348                         struct berval bv;
349                         ldap_back_dn_massage( li, &conn->c_dn, &bv, 0, 1 );
350                         if ( bv.bv_val == conn->c_dn.bv_val ) {
351                                 ber_dupbv( &lc->bound_dn, &bv );
352                         } else {
353                                 lc->bound_dn = bv;
354                         }
355 #endif /* !ENABLE_REWRITE */
356
357                 } else {
358                         lc->bound_dn.bv_val = NULL;
359                         lc->bound_dn.bv_len = 0;
360                 }
361                 lc->bound = 0;
362
363                 /* Inserts the newly created ldapconn in the avl tree */
364                 ldap_pvt_thread_mutex_lock( &li->conn_mutex );
365                 err = avl_insert( &li->conntree, (caddr_t)lc,
366                         ldap_back_conn_cmp, ldap_back_conn_dup );
367
368 #if PRINT_CONNTREE > 0
369                 myprint( li->conntree );
370 #endif /* PRINT_CONNTREE */
371         
372                 ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
373
374 #ifdef NEW_LOGGING
375                 LDAP_LOG( BACK_LDAP, INFO, 
376                         "ldap_back_getconn: conn %lx inserted\n", lc, 0, 0);
377 #else /* !NEW_LOGGING */
378                 Debug( LDAP_DEBUG_TRACE,
379                         "=>ldap_back_getconn: conn %lx inserted\n%s%s",
380                         lc, "", "" );
381 #endif /* !NEW_LOGGING */
382         
383                 /* Err could be -1 in case a duplicate ldapconn is inserted */
384                 if ( err != 0 ) {
385                         ldap_back_conn_free( lc );
386                         send_ldap_result( conn, op, LDAP_OTHER,
387                         NULL, "internal server error", NULL, NULL );
388                         return( NULL );
389                 }
390         } else {
391 #ifdef NEW_LOGGING
392                 LDAP_LOG( BACK_LDAP, INFO, 
393                         "ldap_back_getconn: conn %lx fetched\n", 
394                         lc, 0, 0 );
395 #else /* !NEW_LOGGING */
396                 Debug( LDAP_DEBUG_TRACE,
397                         "=>ldap_back_getconn: conn %lx fetched%s%s\n",
398                         lc, "", "" );
399 #endif /* !NEW_LOGGING */
400         }
401         
402         return( lc );
403 }
404
405 /*
406  * ldap_back_dobind
407  *
408  * Note: as the check for the value of lc->bound was already here, I removed
409  * it from all the callers, and I made the function return the flag, so
410  * it can be used to simplify the check.
411  */
412 int
413 ldap_back_dobind( struct ldapinfo *li, struct ldapconn *lc, Connection *conn, Operation *op )
414 {       
415         int rc;
416         ber_int_t msgid;
417
418         ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
419         if ( !lc->bound ) {
420                 rc = ldap_sasl_bind(lc->ld, lc->bound_dn.bv_val,
421                         LDAP_SASL_SIMPLE, &lc->cred, NULL, NULL, &msgid);
422                 rc = ldap_back_op_result( li, lc, conn, op, msgid, rc );
423                 if (rc == LDAP_SUCCESS) {
424                         lc->bound = 1;
425                 }
426         }
427         rc = lc->bound;
428         ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
429         return rc;
430 }
431
432 /*
433  * ldap_back_rebind
434  *
435  * This is a callback used for chasing referrals using the same
436  * credentials as the original user on this session.
437  */
438 static int 
439 ldap_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
440         ber_int_t msgid, void *params )
441 {
442         struct ldapconn *lc = params;
443
444         return ldap_bind_s( ld, lc->bound_dn.bv_val, lc->cred.bv_val, LDAP_AUTH_SIMPLE );
445 }
446
447 /* Map API errors to protocol errors... */
448
449 int
450 ldap_back_map_result(int err)
451 {
452         switch(err)
453         {
454         case LDAP_SERVER_DOWN:
455                 return LDAP_UNAVAILABLE;
456         case LDAP_LOCAL_ERROR:
457                 return LDAP_OTHER;
458         case LDAP_ENCODING_ERROR:
459         case LDAP_DECODING_ERROR:
460                 return LDAP_PROTOCOL_ERROR;
461         case LDAP_TIMEOUT:
462                 return LDAP_UNAVAILABLE;
463         case LDAP_AUTH_UNKNOWN:
464                 return LDAP_AUTH_METHOD_NOT_SUPPORTED;
465         case LDAP_FILTER_ERROR:
466                 return LDAP_OTHER;
467         case LDAP_USER_CANCELLED:
468                 return LDAP_OTHER;
469         case LDAP_PARAM_ERROR:
470                 return LDAP_PROTOCOL_ERROR;
471         case LDAP_NO_MEMORY:
472                 return LDAP_OTHER;
473         case LDAP_CONNECT_ERROR:
474                 return LDAP_UNAVAILABLE;
475         case LDAP_NOT_SUPPORTED:
476                 return LDAP_UNWILLING_TO_PERFORM;
477         case LDAP_CONTROL_NOT_FOUND:
478                 return LDAP_PROTOCOL_ERROR;
479         case LDAP_NO_RESULTS_RETURNED:
480                 return LDAP_NO_SUCH_OBJECT;
481         case LDAP_MORE_RESULTS_TO_RETURN:
482                 return LDAP_OTHER;
483         case LDAP_CLIENT_LOOP:
484         case LDAP_REFERRAL_LIMIT_EXCEEDED:
485                 return LDAP_LOOP_DETECT;
486         default:
487                 if LDAP_API_ERROR(err)
488                         return LDAP_OTHER;
489                 else
490                         return err;
491         }
492 }
493
494 int
495 ldap_back_op_result(struct ldapinfo *li, struct ldapconn *lc,
496         Connection *conn, Operation *op, ber_int_t msgid, int err)
497 {
498         char *msg = NULL;
499         char *match = NULL;
500         LDAPMessage *res;
501         int rc;
502
503         if (err == LDAP_SUCCESS) {
504                 if (ldap_result(lc->ld, msgid, 1, NULL, &res) == -1) {
505                         ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &err);
506                 } else {
507                         rc = ldap_parse_result(lc->ld, res, &err, &match,
508                                 &msg, NULL, NULL, 1);
509                         if (rc != LDAP_SUCCESS) err = rc;
510                 }
511         }
512         if (err != LDAP_SUCCESS) {
513                 err = ldap_back_map_result(err);
514
515                 /* internal ops must not reply to client */
516                 if ( conn && !op->o_do_not_cache ) {
517                         char *mmatch = NULL;
518 #ifdef ENABLE_REWRITE
519                         if (match) {
520                                 
521                                 switch(rewrite_session(li->rwinfo, "matchedDn", match, conn,
522                                         &mmatch)) {
523                                 case REWRITE_REGEXEC_OK:
524                                         if (!mmatch) mmatch = match; break;
525                                 case REWRITE_REGEXEC_UNWILLING:
526                                 case REWRITE_REGEXEC_ERR:
527                                         break;
528                                 }
529                         }
530 #else
531                         struct berval dn, mdn;
532                         if (match) {
533                                 ber_str2bv(match, 0, 0, &dn);
534                                 ldap_back_dn_massage(li, &dn, &mdn, 0, 0);
535                                 mmatch = mdn.bv_val;
536                         }
537 #endif
538                         send_ldap_result( conn, op, err, mmatch, msg, NULL, NULL );
539                         if (mmatch != match) free(mmatch);
540                 }
541         }
542         if ( match ) free( match );
543         if ( msg ) free( msg );
544         return( (err==LDAP_SUCCESS) ? 0 : -1 );
545 }
546