1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2021 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 
16 #include "portable.h"
17 
18 #include <ac/socket.h>
19 #include <ac/errno.h>
20 #include <ac/string.h>
21 #include <ac/time.h>
22 #include <ac/unistd.h>
23 
24 #include "lutil.h"
25 #include "lload.h"
26 
27 struct berval mech_external = BER_BVC("EXTERNAL");
28 
29 int
bind_mech_external(LloadConnection * client,LloadOperation * op,struct berval * credentials)30 bind_mech_external(
31         LloadConnection *client,
32         LloadOperation *op,
33         struct berval *credentials )
34 {
35     BerValue binddn;
36     void *ssl;
37     char *ptr, *message = "";
38     int result = LDAP_SUCCESS;
39 
40     CONNECTION_ASSERT_LOCKED(client);
41     client->c_state = LLOAD_C_READY;
42     client->c_type = LLOAD_C_OPEN;
43 
44     op->o_res = LLOAD_OP_COMPLETED;
45 
46     /*
47      * We only support implicit assertion.
48      *
49      * Although RFC 4513 says the credentials field must be missing, RFC 4422
50      * doesn't and libsasl2 will pass a zero-length string to send. We have to
51      * allow that.
52      */
53     if ( !BER_BVISEMPTY( credentials ) ) {
54         result = LDAP_UNWILLING_TO_PERFORM;
55         message = "proxy authorization is not supported";
56         goto done;
57     }
58 
59 #ifdef HAVE_TLS
60     ssl = ldap_pvt_tls_sb_ctx( client->c_sb );
61     if ( !ssl || ldap_pvt_tls_get_peer_dn( ssl, &binddn, NULL, 0 ) ) {
62         result = LDAP_INVALID_CREDENTIALS;
63         message = "no externally negotiated identity";
64         goto done;
65     }
66     client->c_auth.bv_len = binddn.bv_len + STRLENOF("dn:");
67     client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 );
68 
69     ptr = lutil_strcopy( client->c_auth.bv_val, "dn:" );
70     ptr = lutil_strncopy( ptr, binddn.bv_val, binddn.bv_len );
71     *ptr = '\0';
72 
73     ber_memfree( binddn.bv_val );
74 
75     if ( !ber_bvstrcasecmp( &client->c_auth, &lloadd_identity ) ) {
76         client->c_type = LLOAD_C_PRIVILEGED;
77     }
78 #else /* ! HAVE_TLS */
79     result = LDAP_AUTH_METHOD_NOT_SUPPORTED;
80     message = "requested SASL mechanism not supported";
81 #endif /* ! HAVE_TLS */
82 
83 done:
84     CONNECTION_UNLOCK(client);
85     operation_send_reject( op, result, message, 1 );
86     return LDAP_SUCCESS;
87 }
88 
89 static int
client_bind(LloadOperation * op,LloadConnection * upstream,struct berval * binddn,ber_tag_t tag,struct berval * auth)90 client_bind(
91         LloadOperation *op,
92         LloadConnection *upstream,
93         struct berval *binddn,
94         ber_tag_t tag,
95         struct berval *auth )
96 {
97     ber_printf( upstream->c_pendingber, "t{titOtO}", LDAP_TAG_MESSAGE,
98             LDAP_TAG_MSGID, op->o_upstream_msgid,
99             LDAP_REQ_BIND, &op->o_request,
100             LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
101 
102     return 0;
103 }
104 
105 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
106 static int
client_bind_as_vc(LloadOperation * op,LloadConnection * upstream,struct berval * binddn,ber_tag_t tag,struct berval * auth)107 client_bind_as_vc(
108         LloadOperation *op,
109         LloadConnection *upstream,
110         struct berval *binddn,
111         ber_tag_t tag,
112         struct berval *auth )
113 {
114     CONNECTION_LOCK(upstream);
115     ber_printf( upstream->c_pendingber, "t{tit{tst{{tOOtOtO}}}}", LDAP_TAG_MESSAGE,
116             LDAP_TAG_MSGID, op->o_upstream_msgid,
117             LDAP_REQ_EXTENDED,
118             LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_VERIFY_CREDENTIALS,
119             LDAP_TAG_EXOP_REQ_VALUE,
120             LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, BER_BV_OPTIONAL( &upstream->c_vc_cookie ),
121             &binddn, tag, &auth,
122             LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
123     CONNECTION_UNLOCK(upstream);
124     return 0;
125 }
126 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
127 
128 /*
129  * The client connection can be in the following states:
130  * 1) there are between zero and many non-bind operations pending
131  *    client->c_state == LLOAD_C_READY && client->c_pin_id == 0
132  * 2) there is one bind operation pending (waiting on an upstream response)
133  *    a) It is a simple bind
134  *    b) It is a SASL bind
135  * 3) there is one SASL bind in progress (received a LDAP_SASL_BIND_IN_PROGRESS
136  *    response)
137  *
138  * In cases 2 and 3, client->c_state == LLOAD_C_BINDING, a SASL bind is in
139  * progress/pending if c_sasl_bind_mech is set.
140  *
141  * In the first case, client_reset abandons all operations on the respective
142  * upstreams, case 2a has client_reset send an anonymous bind to upstream to
143  * terminate the bind. In cases 2b and 3, c_pin_id is set and we retrieve the
144  * op. The rest is the same for both.
145  *
146  * If c_pin_id is unset, we request an upstream connection assigned, otherwise,
147  * we try to reuse the pinned upstream. In the case of no upstream, we reject
148  * the request. A SASL bind request means we acquire a new pin_id if we don't
149  * have one already.
150  *
151  * We have to reset c_auth (which holds the current or pending identity) and
152  * make sure we set it up eventually:
153  * - In the case of a simple bind, we already know the final identity being
154  *   requested so we set it up immediately
155  * - In SASL binds, for mechanisms we implement ourselves (EXTERNAL), we set it
156  *   up at some point
157  * - Otherwise, we have to ask the upstream what it thinks as the bind
158  *   succeeds, we send an LDAP "Who Am I?" exop, this is one of the few
159  *   requests we send on our own. If we implement the mechanism, we provide the
160  *   identity (EXTERNAL uses the client certificate DN)
161  *
162  * At the end of the request processing, if nothing goes wrong, we're in state
163  * 2b (with c_pin_id set to the op's o_pin_id), or state 2a (we could reset
164  * c_pin_id/o_pin_id if we wanted but we don't always do that at the moment).
165  * If something does go wrong, we're either tearing down the client or we
166  * reject the request and switch to state 1 (clearing c_pin_id).
167  *
168  * As usual, we have to make any changes to the target connection before we've
169  * sent the PDU over it - while we are in charge of the read side and nothing
170  * happens there without our ceding control, the other read side could wake up
171  * at any time and preempt us.
172  *
173  * On a response (in handle_bind_response):
174  * - to a simple bind, clear c_auth on a failure otherwise keep it while we
175  *   just reset the client to state 1
176  * - failure response to a SASL bind - reset client to state 1
177  * - LDAP_SASL_BIND_IN_PROGRESS - clear o_*_msgid from the op (have to
178  *   remove+reinsert it from the respective c_ops!), we need it since it is the
179  *   vessel maintaining the pin between client and upstream
180  * - all of the above forward the response immediately
181  * - LDAP_SUCCESS for a SASL bind - we send a "Who Am I?" request to retrieve
182  *   the client's DN, only on receiving the response do we finalise the
183  *   exchange by forwarding the successful bind response
184  *
185  * We can't do the same for VC Exop since the exchange is finished at the end
186  * and we need a change to the VC Exop spec to have the server (optionally?)
187  * respond with the final authzid (saving us a roundtrip as well).
188  */
189 int
request_bind(LloadConnection * client,LloadOperation * op)190 request_bind( LloadConnection *client, LloadOperation *op )
191 {
192     LloadConnection *upstream = NULL;
193     BerElement *ber, *copy;
194     struct berval binddn, auth, mech = BER_BVNULL;
195     ber_int_t version;
196     ber_tag_t tag;
197     unsigned long pin;
198     int res, rc = LDAP_SUCCESS;
199 
200     CONNECTION_LOCK(client);
201     pin = client->c_pin_id;
202 
203     if ( pin ) {
204         LloadOperation *pinned_op, needle = {
205             .o_client_connid = client->c_connid,
206             .o_client_msgid = 0,
207             .o_pin_id = client->c_pin_id,
208         };
209 
210         Debug( LDAP_DEBUG_CONNS, "request_bind: "
211                 "client connid=%lu is pinned pin=%lu\n",
212                 client->c_connid, pin );
213 
214         pinned_op =
215                 ldap_tavl_delete( &client->c_ops, &needle, operation_client_cmp );
216         if ( pinned_op ) {
217             assert( op->o_tag == pinned_op->o_tag );
218 
219             pinned_op->o_client_msgid = op->o_client_msgid;
220 
221             /* Preserve the new BerElement and its pointers, reclaim the old
222              * one in operation_destroy_from_client if it's still there */
223             needle.o_ber = pinned_op->o_ber;
224             pinned_op->o_ber = op->o_ber;
225             op->o_ber = needle.o_ber;
226 
227             pinned_op->o_request = op->o_request;
228             pinned_op->o_ctrls = op->o_ctrls;
229 
230             /* No one has seen this operation yet, plant the pin back in its stead */
231             client->c_n_ops_executing--;
232             op->o_res = LLOAD_OP_COMPLETED;
233             ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
234             op->o_client = NULL;
235             assert( op->o_upstream == NULL );
236 
237             rc = ldap_tavl_insert( &client->c_ops, pinned_op, operation_client_cmp,
238                     ldap_avl_dup_error );
239             assert( rc == LDAP_SUCCESS );
240 
241             /* No one has seen this operation yet */
242             op->o_refcnt--;
243             operation_destroy( op );
244 
245             /* We didn't start a new operation, just continuing an existing one */
246             lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received--;
247 
248             op = pinned_op;
249         }
250     }
251 
252     ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
253     client->c_n_ops_executing--;
254 
255     client_reset( client );
256 
257     client->c_state = LLOAD_C_BINDING;
258     client->c_type = LLOAD_C_OPEN;
259 
260     if ( (copy = ber_alloc()) == NULL ) {
261         goto fail;
262     }
263     ber_init2( copy, &op->o_request, 0 );
264 
265     tag = ber_get_int( copy, &version );
266     if ( tag == LBER_ERROR ) {
267         Debug( LDAP_DEBUG_PACKETS, "request_bind: "
268                 "failed to parse version field\n" );
269         goto fail;
270     } else if ( version != LDAP_VERSION3 ) {
271         CONNECTION_UNLOCK(client);
272         operation_send_reject(
273                 op, LDAP_PROTOCOL_ERROR, "LDAP version unsupported", 1 );
274         CONNECTION_LOCK(client);
275         goto fail;
276     }
277 
278     tag = ber_get_stringbv( copy, &binddn, LBER_BV_NOTERM );
279     if ( tag == LBER_ERROR ) {
280         Debug( LDAP_DEBUG_PACKETS, "request_bind: "
281                 "failed to parse bind name field\n" );
282         goto fail;
283     }
284 
285     if ( !BER_BVISNULL( &client->c_auth ) ) {
286         ch_free( client->c_auth.bv_val );
287         BER_BVZERO( &client->c_auth );
288     }
289 
290     tag = ber_skip_element( copy, &auth );
291     if ( tag == LDAP_AUTH_SIMPLE ) {
292         if ( !BER_BVISEMPTY( &binddn ) ) {
293             char *ptr;
294             client->c_auth.bv_len = STRLENOF("dn:") + binddn.bv_len;
295             client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 );
296 
297             ptr = lutil_strcopy( client->c_auth.bv_val, "dn:" );
298             ptr = lutil_strncopy( ptr, binddn.bv_val, binddn.bv_len );
299             *ptr = '\0';
300         }
301 
302         if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
303             ber_memfree( client->c_sasl_bind_mech.bv_val );
304             BER_BVZERO( &client->c_sasl_bind_mech );
305         }
306     } else if ( tag == LDAP_AUTH_SASL ) {
307         ber_init2( copy, &auth, 0 );
308 
309         if ( ber_get_stringbv( copy, &mech, LBER_BV_NOTERM ) == LBER_ERROR ) {
310             goto fail;
311         }
312         if ( !ber_bvcmp( &mech, &mech_external ) ) {
313             struct berval credentials = BER_BVNULL;
314 
315             ber_get_stringbv( copy, &credentials, LBER_BV_NOTERM );
316             rc = bind_mech_external( client, op, &credentials );
317 
318             /* terminate the upstream side if client switched mechanisms */
319             if ( pin ) {
320                 operation_abandon( op );
321             }
322 
323             ber_free( copy, 0 );
324             return rc;
325         } else if ( BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
326             ber_dupbv( &client->c_sasl_bind_mech, &mech );
327         } else if ( ber_bvcmp( &mech, &client->c_sasl_bind_mech ) ) {
328             ber_bvreplace( &client->c_sasl_bind_mech, &mech );
329         }
330     } else {
331         goto fail;
332     }
333 
334     rc = ldap_tavl_insert( &client->c_ops, op, operation_client_cmp, ldap_avl_dup_error );
335     assert( rc == LDAP_SUCCESS );
336     client->c_n_ops_executing++;
337     CONNECTION_UNLOCK(client);
338 
339     if ( pin ) {
340         checked_lock( &op->o_link_mutex );
341         upstream = op->o_upstream;
342         checked_unlock( &op->o_link_mutex );
343 
344         if ( upstream ) {
345             checked_lock( &upstream->c_io_mutex );
346             CONNECTION_LOCK(upstream);
347             if ( !IS_ALIVE( upstream, c_live ) ) {
348                 CONNECTION_UNLOCK(upstream);
349                 checked_unlock( &upstream->c_io_mutex );
350                 upstream = NULL;
351             }
352         }
353     }
354 
355     /* If we were pinned but lost the link, don't look for a new upstream, we
356      * have to reject the op and clear pin */
357     if ( upstream ) {
358         /* No need to do anything */
359     } else if ( !pin ) {
360         upstream = backend_select( op, &res );
361     } else {
362         Debug( LDAP_DEBUG_STATS, "request_bind: "
363                 "connid=%lu, msgid=%d pinned upstream lost\n",
364                 op->o_client_connid, op->o_client_msgid );
365         operation_send_reject( op, LDAP_OTHER,
366                 "connection to the remote server has been severed", 1 );
367         pin = 0;
368         goto done;
369     }
370 
371     if ( !upstream ) {
372         Debug( LDAP_DEBUG_STATS, "request_bind: "
373                 "connid=%lu, msgid=%d no available connection found\n",
374                 op->o_client_connid, op->o_client_msgid );
375         operation_send_reject( op, res, "no connections available", 1 );
376         assert( client->c_pin_id == 0 );
377         goto done;
378     }
379     assert_locked( &upstream->c_io_mutex );
380     /*
381      * At this point, either:
382      * - upstream is READY and pin == 0
383      * - upstream is BINDING, pin != 0 and op->o_upstream_msgid == 0
384      *
385      * A pinned upstream we marked for closing at some point ago should have
386      * closed by now.
387      */
388 
389     ber = upstream->c_pendingber;
390     if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
391         checked_unlock( &upstream->c_io_mutex );
392         if ( !pin ) {
393             LloadBackend *b = upstream->c_backend;
394 
395             upstream->c_n_ops_executing--;
396             CONNECTION_UNLOCK(upstream);
397 
398             checked_lock( &b->b_mutex );
399             b->b_n_ops_executing--;
400             operation_update_backend_counters( op, b );
401             checked_unlock( &b->b_mutex );
402         } else {
403             CONNECTION_UNLOCK(upstream);
404         }
405 
406         Debug( LDAP_DEBUG_ANY, "request_bind: "
407                 "ber_alloc failed\n" );
408 
409         operation_unlink( op );
410 
411         CONNECTION_LOCK(client);
412         goto fail;
413     }
414     upstream->c_pendingber = ber;
415 
416     if ( !pin ) {
417         lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_forwarded++;
418     }
419 
420     if ( pin ) {
421         ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
422         if ( tag == LDAP_AUTH_SIMPLE ) {
423             pin = op->o_pin_id = 0;
424         }
425     } else if ( tag == LDAP_AUTH_SASL && !op->o_pin_id ) {
426         checked_lock( &lload_pin_mutex );
427         pin = op->o_pin_id = lload_next_pin++;
428         Debug( LDAP_DEBUG_CONNS, "request_bind: "
429                 "client connid=%lu allocated pin=%lu linking it to upstream "
430                 "connid=%lu\n",
431                 op->o_client_connid, pin, upstream->c_connid );
432         checked_unlock( &lload_pin_mutex );
433     }
434 
435     op->o_upstream = upstream;
436     op->o_upstream_connid = upstream->c_connid;
437     op->o_upstream_msgid = upstream->c_next_msgid++;
438     op->o_res = LLOAD_OP_FAILED;
439 
440     /* Was it unlinked in the meantime? No need to send a response since the
441      * client is dead */
442     if ( !IS_ALIVE( op, o_refcnt ) ) {
443         LloadBackend *b = upstream->c_backend;
444 
445         upstream->c_n_ops_executing--;
446         checked_unlock( &upstream->c_io_mutex );
447         CONNECTION_UNLOCK(upstream);
448 
449         checked_lock( &b->b_mutex );
450         b->b_n_ops_executing--;
451         checked_unlock( &b->b_mutex );
452 
453         assert( !IS_ALIVE( client, c_live ) );
454         checked_lock( &op->o_link_mutex );
455         if ( op->o_upstream ) {
456             op->o_upstream = NULL;
457         }
458         checked_unlock( &op->o_link_mutex );
459         rc = -1;
460         goto done;
461     }
462 
463     if ( BER_BVISNULL( &mech ) ) {
464         if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
465             ber_memfree( upstream->c_sasl_bind_mech.bv_val );
466             BER_BVZERO( &upstream->c_sasl_bind_mech );
467         }
468     } else if ( ber_bvcmp( &upstream->c_sasl_bind_mech, &mech ) ) {
469         ber_bvreplace( &upstream->c_sasl_bind_mech, &mech );
470     }
471 
472     Debug( LDAP_DEBUG_TRACE, "request_bind: "
473             "added bind from client connid=%lu to upstream connid=%lu "
474             "as msgid=%d\n",
475             op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid );
476     if ( ldap_tavl_insert( &upstream->c_ops, op, operation_upstream_cmp,
477                  ldap_avl_dup_error ) ) {
478         assert(0);
479     }
480     upstream->c_state = LLOAD_C_BINDING;
481     CONNECTION_UNLOCK(upstream);
482 
483 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
484     if ( lload_features & LLOAD_FEATURE_VC ) {
485         rc = client_bind_as_vc( op, upstream, &binddn, tag, &auth );
486     } else
487 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
488     {
489         rc = client_bind( op, upstream, &binddn, tag, &auth );
490     }
491     checked_unlock( &upstream->c_io_mutex );
492 
493 done:
494 
495     CONNECTION_LOCK(client);
496     if ( rc == LDAP_SUCCESS ) {
497         client->c_pin_id = pin;
498         CONNECTION_UNLOCK(client);
499 
500         if ( upstream ) {
501             connection_write_cb( -1, 0, upstream );
502         }
503     } else {
504 fail:
505         rc = -1;
506 
507         client->c_pin_id = 0;
508         CONNECTION_DESTROY(client);
509     }
510 
511     ber_free( copy, 0 );
512     return rc;
513 }
514 
515 /*
516  * Remember the response, but first ask the server what
517  * authorization identity has been negotiated.
518  *
519  * Also, this request will fail if the server thinks a SASL
520  * confidentiality/integrity layer has been negotiated so we catch
521  * it early and no other clients are affected.
522  */
523 int
finish_sasl_bind(LloadConnection * upstream,LloadOperation * op,BerElement * ber)524 finish_sasl_bind(
525         LloadConnection *upstream,
526         LloadOperation *op,
527         BerElement *ber )
528 {
529     BerElement *output;
530     LloadOperation *removed;
531     ber_int_t msgid;
532     int rc;
533 
534     CONNECTION_ASSERT_LOCKED(upstream);
535     removed = ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
536     if ( !removed ) {
537         assert( upstream->c_state != LLOAD_C_BINDING );
538         /* FIXME: has client replaced this bind since? */
539         assert(0);
540     }
541     assert( removed == op && upstream->c_state == LLOAD_C_BINDING );
542 
543     CONNECTION_UNLOCK(upstream);
544 
545     checked_lock( &upstream->c_io_mutex );
546     output = upstream->c_pendingber;
547     if ( output == NULL && (output = ber_alloc()) == NULL ) {
548         checked_unlock( &upstream->c_io_mutex );
549         CONNECTION_LOCK_DESTROY(upstream);
550         return -1;
551     }
552     upstream->c_pendingber = output;
553 
554     msgid = upstream->c_next_msgid++;
555     ber_printf( output, "t{tit{ts}}", LDAP_TAG_MESSAGE,
556             LDAP_TAG_MSGID, msgid,
557             LDAP_REQ_EXTENDED,
558             LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_WHO_AM_I );
559 
560     /* Make sure noone flushes the buffer before we re-insert the operation */
561     CONNECTION_LOCK(upstream);
562     checked_unlock( &upstream->c_io_mutex );
563 
564     op->o_upstream_msgid = msgid;
565 
566     /* remember the response for later */
567     ber_free( op->o_ber, 1 );
568     op->o_ber = ber;
569 
570     /* Could we have been unlinked in the meantime? */
571     rc = ldap_tavl_insert(
572             &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error );
573     assert( rc == LDAP_SUCCESS );
574 
575     CONNECTION_UNLOCK(upstream);
576 
577     Debug( LDAP_DEBUG_TRACE, "finish_sasl_bind: "
578             "SASL exchange in lieu of client connid=%lu to upstream "
579             "connid=%lu finished, resolving final authzid name msgid=%d\n",
580             op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid );
581 
582     connection_write_cb( -1, 0, upstream );
583     return LDAP_SUCCESS;
584 }
585 
586 int
handle_bind_response(LloadConnection * client,LloadOperation * op,BerElement * ber)587 handle_bind_response(
588         LloadConnection *client,
589         LloadOperation *op,
590         BerElement *ber )
591 {
592     LloadConnection *upstream;
593     BerValue response;
594     BerElement *copy;
595     LloadOperation *removed;
596     ber_int_t result;
597     ber_tag_t tag;
598     int rc = LDAP_SUCCESS;
599 
600     if ( (copy = ber_alloc()) == NULL ) {
601         rc = -1;
602         goto done;
603     }
604 
605     tag = ber_peek_element( ber, &response );
606     assert( tag == LDAP_RES_BIND );
607 
608     ber_init2( copy, &response, 0 );
609 
610     tag = ber_get_enum( copy, &result );
611     ber_free( copy, 0 );
612 
613     if ( tag == LBER_ERROR ) {
614         rc = -1;
615         goto done;
616     }
617 
618     Debug( LDAP_DEBUG_STATS, "handle_bind_response: "
619             "received response for bind request msgid=%d by client "
620             "connid=%lu, result=%d\n",
621             op->o_client_msgid, op->o_client_connid, result );
622 
623     checked_lock( &op->o_link_mutex );
624     upstream = op->o_upstream;
625     checked_unlock( &op->o_link_mutex );
626     if ( !upstream ) {
627         return LDAP_SUCCESS;
628     }
629 
630     CONNECTION_LOCK(upstream);
631     if ( !ldap_tavl_find( upstream->c_ops, op, operation_upstream_cmp ) ) {
632         /*
633          * operation might not be found because:
634          * - it has timed out (only happens when debugging/hung/...)
635          *   a response has been sent for us, we must not send another
636          * - it has been abandoned (new bind, unbind)
637          *   no response is expected
638          * - ???
639          */
640         CONNECTION_UNLOCK(upstream);
641         return LDAP_SUCCESS;
642     }
643 
644     /*
645      * We might be marked for closing, forward the response if we can, but do
646      * no more if it's a SASL bind - just finish the operation and send failure
647      * in that case (since we can't resolve the bind identity correctly).
648      */
649     if ( upstream->c_state == LLOAD_C_CLOSING ) {
650         /* FIXME: this is too ad-hoc */
651         if ( ( result == LDAP_SUCCESS ||
652                      result == LDAP_SASL_BIND_IN_PROGRESS ) &&
653                 !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
654             CONNECTION_UNLOCK(upstream);
655             operation_send_reject(
656                     op, LDAP_OTHER, "upstream connection is closing", 0 );
657 
658             ber_free( ber, 1 );
659             return LDAP_SUCCESS;
660         }
661 
662         assert( op->o_client_msgid && op->o_upstream_msgid );
663         op->o_pin_id = 0;
664 
665     } else if ( result == LDAP_SASL_BIND_IN_PROGRESS ) {
666         ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
667         op->o_upstream_msgid = 0;
668         rc = ldap_tavl_insert(
669                 &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error );
670         assert( rc == LDAP_SUCCESS );
671     } else {
672         int sasl_finished = 0;
673         if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
674             sasl_finished = 1;
675             ber_memfree( upstream->c_sasl_bind_mech.bv_val );
676             BER_BVZERO( &upstream->c_sasl_bind_mech );
677         }
678 
679         assert( op->o_client_msgid && op->o_upstream_msgid );
680         op->o_pin_id = 0;
681 
682         if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) && sasl_finished &&
683                 result == LDAP_SUCCESS ) {
684             return finish_sasl_bind( upstream, op, ber );
685         }
686         op->o_res = LLOAD_OP_COMPLETED;
687     }
688     CONNECTION_UNLOCK(upstream);
689 
690     if ( !op->o_pin_id ) {
691         operation_unlink_upstream( op, upstream );
692     }
693 
694     CONNECTION_LOCK(client);
695     removed = ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
696     assert( !removed || op == removed );
697 
698     if ( client->c_state == LLOAD_C_BINDING ) {
699         assert( removed );
700         switch ( result ) {
701             case LDAP_SASL_BIND_IN_PROGRESS:
702                 op->o_saved_msgid = op->o_client_msgid;
703                 op->o_client_msgid = 0;
704                 rc = ldap_tavl_insert( &client->c_ops, op, operation_client_cmp,
705                         ldap_avl_dup_error );
706                 assert( rc == LDAP_SUCCESS );
707                 break;
708             case LDAP_SUCCESS:
709             default: {
710                 client->c_state = LLOAD_C_READY;
711                 client->c_type = LLOAD_C_OPEN;
712                 client->c_pin_id = 0;
713                 client->c_n_ops_executing--;
714                 if ( !BER_BVISNULL( &client->c_auth ) ) {
715                     if ( result != LDAP_SUCCESS ) {
716                         ber_memfree( client->c_auth.bv_val );
717                         BER_BVZERO( &client->c_auth );
718                     } else if ( !ber_bvstrcasecmp(
719                                         &client->c_auth, &lloadd_identity ) ) {
720                         client->c_type = LLOAD_C_PRIVILEGED;
721                     }
722                 }
723                 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
724                     ber_memfree( client->c_sasl_bind_mech.bv_val );
725                     BER_BVZERO( &client->c_sasl_bind_mech );
726                 }
727                 break;
728             }
729         }
730     } else {
731         if ( removed ) {
732             client->c_n_ops_executing--;
733         }
734         assert( client->c_state == LLOAD_C_DYING ||
735                 client->c_state == LLOAD_C_CLOSING );
736     }
737     CONNECTION_UNLOCK(client);
738 
739 done:
740     if ( rc ) {
741         operation_send_reject( op, LDAP_OTHER, "internal error", 1 );
742 
743         ber_free( ber, 1 );
744         return LDAP_SUCCESS;
745     }
746     return forward_final_response( client, op, ber );
747 }
748 
749 int
handle_whoami_response(LloadConnection * client,LloadOperation * op,BerElement * ber)750 handle_whoami_response(
751         LloadConnection *client,
752         LloadOperation *op,
753         BerElement *ber )
754 {
755     LloadConnection *upstream;
756     BerValue matched, diagmsg;
757     BerElement *saved_response = op->o_ber;
758     LloadOperation *removed;
759     ber_int_t result;
760     ber_tag_t tag;
761     ber_len_t len;
762 
763     Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
764             "connid=%ld received whoami response in lieu of connid=%ld\n",
765             op->o_upstream_connid, client->c_connid );
766 
767     tag = ber_scanf( ber, "{emm" /* "}" */,
768             &result, &matched, &diagmsg );
769     if ( tag == LBER_ERROR ) {
770         operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 );
771         return -1;
772     }
773 
774     checked_lock( &op->o_link_mutex );
775     upstream = op->o_upstream;
776     checked_unlock( &op->o_link_mutex );
777     if ( !upstream ) {
778         return LDAP_SUCCESS;
779     }
780 
781     op->o_res = LLOAD_OP_COMPLETED;
782     /* Clear upstream status */
783     operation_unlink_upstream( op, upstream );
784 
785     if ( result == LDAP_PROTOCOL_ERROR ) {
786         LloadBackend *b;
787 
788         CONNECTION_LOCK(upstream);
789         b = upstream->c_backend;
790         Debug( LDAP_DEBUG_ANY, "handle_whoami_response: "
791                 "Who Am I? extended operation not supported on backend %s, "
792                 "proxyauthz with clients that do SASL binds will not work "
793                 "msg=%s!\n",
794                 b->b_uri.bv_val, diagmsg.bv_val );
795         CONNECTION_UNLOCK(upstream);
796         operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 );
797         return -1;
798     }
799 
800     tag = ber_peek_tag( ber, &len );
801 
802     CONNECTION_LOCK(client);
803 
804     assert( client->c_state == LLOAD_C_BINDING ||
805             client->c_state == LLOAD_C_CLOSING );
806 
807     assert( BER_BVISNULL( &client->c_auth ) );
808     if ( !BER_BVISNULL( &client->c_auth ) ) {
809         ber_memfree( client->c_auth.bv_val );
810         BER_BVZERO( &client->c_auth );
811     }
812 
813     if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
814         tag = ber_scanf( ber, "o", &client->c_auth );
815         if ( tag == LBER_ERROR ) {
816             CONNECTION_DESTROY(client);
817             return -1;
818         }
819     }
820 
821     removed = ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
822     assert( !removed || op == removed );
823     op->o_pin_id = 0;
824     if ( removed ) {
825         client->c_n_ops_executing--;
826     }
827 
828     Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
829             "connid=%ld new authid=%s\n",
830             client->c_connid, client->c_auth.bv_val );
831 
832     if ( client->c_state == LLOAD_C_BINDING ) {
833         client->c_state = LLOAD_C_READY;
834         client->c_type = LLOAD_C_OPEN;
835         client->c_pin_id = 0;
836         if ( !BER_BVISNULL( &client->c_auth ) &&
837                 !ber_bvstrcasecmp( &client->c_auth, &lloadd_identity ) ) {
838             client->c_type = LLOAD_C_PRIVILEGED;
839         }
840         if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
841             ber_memfree( client->c_sasl_bind_mech.bv_val );
842             BER_BVZERO( &client->c_sasl_bind_mech );
843         }
844     }
845 
846     CONNECTION_UNLOCK(client);
847 
848     /* defer the disposal of ber to operation_destroy */
849     op->o_ber = ber;
850 
851     return forward_final_response( client, op, saved_response );
852 }
853 
854 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
855 int
handle_vc_bind_response(LloadConnection * client,LloadOperation * op,BerElement * ber)856 handle_vc_bind_response(
857         LloadConnection *client,
858         LloadOperation *op,
859         BerElement *ber )
860 {
861     BerElement *output;
862     BerValue matched, diagmsg, creds = BER_BVNULL, controls = BER_BVNULL;
863     ber_int_t result;
864     ber_tag_t tag;
865     ber_len_t len;
866     int rc = 0;
867 
868     tag = ber_scanf( ber, "{emm" /* "}" */,
869             &result, &matched, &diagmsg );
870     if ( tag == LBER_ERROR ) {
871         rc = -1;
872         goto done;
873     }
874 
875     tag = ber_peek_tag( ber, &len );
876     if ( result == LDAP_PROTOCOL_ERROR ) {
877         LloadConnection *upstream;
878 
879         checked_lock( &op->o_link_mutex );
880         upstream = op->o_upstream;
881         checked_unlock( &op->o_link_mutex );
882         if ( upstream ) {
883             LloadBackend *b;
884 
885             CONNECTION_LOCK(upstream);
886             b = upstream->c_backend;
887             Debug( LDAP_DEBUG_ANY, "handle_vc_bind_response: "
888                     "VC extended operation not supported on backend %s\n",
889                     b->b_uri.bv_val );
890             CONNECTION_UNLOCK(upstream);
891         }
892     }
893 
894     Debug( LDAP_DEBUG_STATS, "handle_vc_bind_response: "
895             "received response for bind request msgid=%d by client "
896             "connid=%lu, result=%d\n",
897             op->o_client_msgid, op->o_client_connid, result );
898 
899     CONNECTION_LOCK(client);
900 
901     if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) {
902         if ( !BER_BVISNULL( &client->c_vc_cookie ) ) {
903             ber_memfree( client->c_vc_cookie.bv_val );
904         }
905         tag = ber_scanf( ber, "o", &client->c_vc_cookie );
906         if ( tag == LBER_ERROR ) {
907             rc = -1;
908             CONNECTION_UNLOCK(client);
909             goto done;
910         }
911         tag = ber_peek_tag( ber, &len );
912     }
913 
914     if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS ) {
915         tag = ber_scanf( ber, "m", &creds );
916         if ( tag == LBER_ERROR ) {
917             rc = -1;
918             CONNECTION_UNLOCK(client);
919             goto done;
920         }
921         tag = ber_peek_tag( ber, &len );
922     }
923 
924     if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) {
925         tag = ber_scanf( ber, "m", &controls );
926         if ( tag == LBER_ERROR ) {
927             rc = -1;
928             CONNECTION_UNLOCK(client);
929             goto done;
930         }
931     }
932 
933     if ( client->c_state == LLOAD_C_BINDING ) {
934         switch ( result ) {
935             case LDAP_SASL_BIND_IN_PROGRESS:
936                 break;
937             case LDAP_SUCCESS:
938             default: {
939                 client->c_state = LLOAD_C_READY;
940                 client->c_type = LLOAD_C_OPEN;
941                 client->c_pin_id = 0;
942                 if ( result != LDAP_SUCCESS ) {
943                     ber_memfree( client->c_auth.bv_val );
944                     BER_BVZERO( &client->c_auth );
945                 } else if ( !ber_bvstrcasecmp(
946                                     &client->c_auth, &lloadd_identity ) ) {
947                     client->c_type = LLOAD_C_PRIVILEGED;
948                 }
949                 if ( !BER_BVISNULL( &client->c_vc_cookie ) ) {
950                     ber_memfree( client->c_vc_cookie.bv_val );
951                     BER_BVZERO( &client->c_vc_cookie );
952                 }
953                 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
954                     ber_memfree( client->c_sasl_bind_mech.bv_val );
955                     BER_BVZERO( &client->c_sasl_bind_mech );
956                 }
957                 break;
958             }
959         }
960     } else {
961         assert( client->c_state == LLOAD_C_INVALID ||
962                 client->c_state == LLOAD_C_CLOSING );
963     }
964     CONNECTION_UNLOCK(client);
965 
966     checked_lock( &client->c_io_mutex );
967     output = client->c_pendingber;
968     if ( output == NULL && (output = ber_alloc()) == NULL ) {
969         rc = -1;
970         checked_unlock( &client->c_io_mutex );
971         goto done;
972     }
973     client->c_pendingber = output;
974 
975     rc = ber_printf( output, "t{tit{eOOtO}tO}", LDAP_TAG_MESSAGE,
976             LDAP_TAG_MSGID, op->o_client_msgid, LDAP_RES_BIND,
977             result, &matched, &diagmsg,
978             LDAP_TAG_SASL_RES_CREDS, BER_BV_OPTIONAL( &creds ),
979             LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &controls ) );
980 
981     checked_unlock( &client->c_io_mutex );
982     if ( rc >= 0 ) {
983         connection_write_cb( -1, 0, client );
984         rc = 0;
985     }
986 
987 done:
988     operation_unlink( op );
989     ber_free( ber, 1 );
990     return rc;
991 }
992 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
993