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