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