]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/bind.c
add administrative bind and proxyAuthz control to enable bound operations in distribu...
[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         struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
408         int rc;
409         ber_int_t msgid;
410
411         ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
412         if ( !lc->bound ) {
413 #ifdef LDAP_BACK_PROXY_AUTHZ
414                 int     gotit = 0;
415 #if 0
416                 int     i;
417
418                 /*
419                  * FIXME: we need to let clients use proxyAuthz
420                  * otherwise we cannot do symmetric pools of servers;
421                  * we have to live with the fact that a user can
422                  * authorize itself as any ID that is allowed
423                  * by the saslAuthzTo directive of the "binddn".
424                  */
425                 for ( i = 0; op->o_ctrls && op->o_ctrls[ i ]; i++ ) {
426                         if ( strcmp( op->o_ctrls[i]->ldctl_oid, LDAP_CONTROL_PROXY_AUTHZ ) == 0 ) {
427                                 gotit = 1;
428                                 break;
429                         }
430                 }
431 #endif
432
433                 /*
434                  * if no bind took place yet, but the connection is bound
435                  * and the binddn is set, then bind with binddn and 
436                  * explicitly add proxyAuthz control to every operation
437                  * with the dn bound to the connection as control value.
438                  */
439                 if ( ( lc->bound_dn.bv_val == NULL || lc->bound_dn.bv_len == 0 )
440                                 && ( op->o_conn && op->o_conn->c_dn.bv_val != NULL && op->o_conn->c_dn.bv_len != 0 )
441                                 && ( li->binddn.bv_val != NULL && li->binddn.bv_len != 0 ) 
442                                 && ! gotit ) {
443                         rs->sr_err = ldap_sasl_bind(lc->ld, li->binddn.bv_val,
444                                 LDAP_SASL_SIMPLE, &li->bindpw, NULL, NULL, &msgid);
445
446                 } else
447 #endif /* LDAP_BACK_PROXY_AUTHZ */
448                 {
449                         rs->sr_err = ldap_sasl_bind(lc->ld, lc->bound_dn.bv_val,
450                                 LDAP_SASL_SIMPLE, &lc->cred, NULL, NULL, &msgid);
451                 }
452                 
453                 rc = ldap_back_op_result( lc, op, rs, msgid, 0 );
454                 if (rc == LDAP_SUCCESS) {
455                         lc->bound = 1;
456                 }
457         }
458         rc = lc->bound;
459         ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
460         return rc;
461 }
462
463 /*
464  * ldap_back_rebind
465  *
466  * This is a callback used for chasing referrals using the same
467  * credentials as the original user on this session.
468  */
469 static int 
470 ldap_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
471         ber_int_t msgid, void *params )
472 {
473         struct ldapconn *lc = params;
474
475         return ldap_bind_s( ld, lc->bound_dn.bv_val, lc->cred.bv_val, LDAP_AUTH_SIMPLE );
476 }
477
478 /* Map API errors to protocol errors... */
479
480 int
481 ldap_back_map_result(SlapReply *rs)
482 {
483         switch(rs->sr_err)
484         {
485         case LDAP_SERVER_DOWN:
486                 return LDAP_UNAVAILABLE;
487         case LDAP_LOCAL_ERROR:
488                 return LDAP_OTHER;
489         case LDAP_ENCODING_ERROR:
490         case LDAP_DECODING_ERROR:
491                 return LDAP_PROTOCOL_ERROR;
492         case LDAP_TIMEOUT:
493                 return LDAP_UNAVAILABLE;
494         case LDAP_AUTH_UNKNOWN:
495                 return LDAP_AUTH_METHOD_NOT_SUPPORTED;
496         case LDAP_FILTER_ERROR:
497                 rs->sr_text = "Filter error";
498                 return LDAP_OTHER;
499         case LDAP_USER_CANCELLED:
500                 rs->sr_text = "User cancelled";
501                 return LDAP_OTHER;
502         case LDAP_PARAM_ERROR:
503                 return LDAP_PROTOCOL_ERROR;
504         case LDAP_NO_MEMORY:
505                 return LDAP_OTHER;
506         case LDAP_CONNECT_ERROR:
507                 return LDAP_UNAVAILABLE;
508         case LDAP_NOT_SUPPORTED:
509                 return LDAP_UNWILLING_TO_PERFORM;
510         case LDAP_CONTROL_NOT_FOUND:
511                 return LDAP_PROTOCOL_ERROR;
512         case LDAP_NO_RESULTS_RETURNED:
513                 return LDAP_NO_SUCH_OBJECT;
514         case LDAP_MORE_RESULTS_TO_RETURN:
515                 rs->sr_text = "More results to return";
516                 return LDAP_OTHER;
517         case LDAP_CLIENT_LOOP:
518         case LDAP_REFERRAL_LIMIT_EXCEEDED:
519                 return LDAP_LOOP_DETECT;
520         default:
521                 if LDAP_API_ERROR(rs->sr_err)
522                         return LDAP_OTHER;
523                 else
524                         return rs->sr_err;
525         }
526 }
527
528 int
529 ldap_back_op_result(struct ldapconn *lc, Operation *op, SlapReply *rs,
530         ber_int_t msgid, int sendok)
531 {
532         struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
533         char *match = NULL;
534         LDAPMessage *res;
535         char *text = NULL;
536
537         rs->sr_text = NULL;
538         rs->sr_matched = NULL;
539
540         if (rs->sr_err == LDAP_SUCCESS) {
541                 if (ldap_result(lc->ld, msgid, 1, NULL, &res) == -1) {
542                         ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
543                                         &rs->sr_err);
544                 } else {
545                         int rc = ldap_parse_result(lc->ld, res, &rs->sr_err,
546                                         &match, &text, NULL, NULL, 1);
547                         rs->sr_text = text;
548                         if (rc != LDAP_SUCCESS) rs->sr_err = rc;
549                 }
550         }
551
552         if (rs->sr_err != LDAP_SUCCESS) {
553                 rs->sr_err = ldap_back_map_result(rs);
554
555                 /* internal ops must not reply to client */
556                 if ( op->o_conn && !op->o_do_not_cache && match ) {
557                         struct berval dn, mdn;
558                         dncookie dc;
559
560                         dc.rwmap = &li->rwmap;
561 #ifdef ENABLE_REWRITE
562                         dc.conn = op->o_conn;
563                         dc.rs = rs;
564                         dc.ctx = "matchedDn";
565 #else
566                         dc.tofrom = 0;
567                         dc.normalized = 0;
568 #endif
569                         ber_str2bv(match, 0, 0, &dn);
570                         ldap_back_dn_massage(&dc, &dn, &mdn);
571                         rs->sr_matched = mdn.bv_val;
572                                 
573                 }
574         }
575         if (op->o_conn && (sendok || rs->sr_err != LDAP_SUCCESS)) {
576                 send_ldap_result( op, rs );
577         }
578         if ( match ) {
579                 if ( rs->sr_matched != match ) {
580                         free( (char *)rs->sr_matched );
581                 }
582                 rs->sr_matched = NULL;
583                 ldap_memfree( match );
584         }
585         if ( text ) {
586                 ldap_memfree( text );
587         }
588         rs->sr_text = NULL;
589         return( (rs->sr_err == LDAP_SUCCESS) ? 0 : -1 );
590 }
591
592 #ifdef LDAP_BACK_PROXY_AUTHZ
593 /*
594  * ldap_back_proxy_authz_ctrl() prepends a proxyAuthz control
595  * to existing server-side controls if required; if not,
596  * the existing server-side controls are placed in *pctrls.
597  * The caller, after using the controls in client API 
598  * operations, if ( *pctrls != op->o_ctrls ), should
599  * free( (*pctrls)[ 0 ] ) and free( *pctrls ).
600  * The function returns success if the control could
601  * be added if required, or if it did nothing; in the future,
602  * it might return some error if it failed.
603  * 
604  * if no bind took place yet, but the connection is bound
605  * and the binddn is set, then bind with binddn and 
606  * explicitly add proxyAuthz control to every operation
607  * with the dn bound to the connection as control value.
608  *
609  * If no server-side controls are defined for the operation,
610  * simply add the proxyAuthz control; otherwise, if the
611  * proxyAuthz control is not already set, add it as
612  * the first one (FIXME: is controls order significant
613  * for security?).
614  */
615 int
616 ldap_back_proxy_authz_ctrl(
617                 struct ldapconn *lc,
618                 Operation       *op,
619                 SlapReply       *rs,
620                 LDAPControl     ***pctrls )
621 {
622         struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
623         LDAPControl     **ctrls = NULL;
624
625         *pctrls = NULL;
626
627         if ( ( lc->bound_dn.bv_val == NULL || lc->bound_dn.bv_len == 0 )
628                         && ( op->o_conn && op->o_conn->c_dn.bv_val != NULL && op->o_conn->c_dn.bv_len != 0 )
629                         && ( li->binddn.bv_val != NULL && li->binddn.bv_len != 0 ) ) {
630                 int     i = 0, gotit = 0;
631                 
632                 if ( op->o_ctrls ) {
633                         for ( i = 0; op->o_ctrls[i]; i++ ) {
634                                 if ( strcmp( op->o_ctrls[i]->ldctl_oid, LDAP_CONTROL_PROXY_AUTHZ ) == 0 ) {
635                                         gotit = 1;
636                                         break;
637                                 }
638                         }
639                 }
640
641                 if ( ! gotit ) {
642                         ctrls = ch_malloc( sizeof( LDAPControl * ) * (i + 2) );
643                         ctrls[ 0 ] = ch_malloc( sizeof( LDAPControl ) );
644                         
645                         ctrls[ 0 ]->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
646                         ctrls[ 0 ]->ldctl_iscritical = 1;
647                         ctrls[ 0 ]->ldctl_value.bv_len = op->o_conn->c_dn.bv_len + 3;
648                         ctrls[ 0 ]->ldctl_value.bv_val = ch_malloc( ctrls[ 0 ]->ldctl_value.bv_len + 1 );
649                         AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val, "dn:", sizeof( "dn:" ) - 1 );
650                         AC_MEMCPY( ctrls[ 0 ]->ldctl_value.bv_val + sizeof( "dn:") - 1,
651                                         op->o_conn->c_dn.bv_val, op->o_conn->c_dn.bv_len + 1 );
652
653                         if ( op->o_ctrls ) {
654                                 for ( i = 0; op->o_ctrls[ i ]; i++ ) {
655                                         ctrls[ i + 1 ] = op->o_ctrls[ i ];
656                                 }
657                         }
658                         ctrls[ i + 1 ] = NULL;
659
660                 } else {
661                         /*
662                          * FIXME: we do not want to perform proxyAuthz
663                          * on behalf of the client, because this would
664                          * be performed with "binddn" privileges.
665                          *
666                          * This might actually be too strict, since
667                          * the "binddn" saslAuthzTo, and each entry's
668                          * saslAuthzFrom attributes may be crafted
669                          * to avoid unwanted proxyAuthz to take place.
670                          */
671 #if 0
672                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
673                         rs->sr_text = "proxyAuthz not allowed within namingContext";
674 #endif
675                 }
676         }
677
678         if ( ctrls == NULL ) {
679                 ctrls = op->o_ctrls;
680         }
681
682         *pctrls = ctrls;
683         
684         return rs->sr_err;
685 }
686 #endif /* LDAP_BACK_PROXY_AUTHZ */