1 /* $NetBSD: client.c,v 1.2 2021/08/14 16:14:58 christos Exp $ */
2
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1998-2021 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: client.c,v 1.2 2021/08/14 16:14:58 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 long lload_client_max_pending = 0;
33
34 lload_c_head clients = LDAP_CIRCLEQ_HEAD_INITIALIZER( clients );
35
36 ldap_pvt_thread_mutex_t clients_mutex;
37
38 static void client_unlink( LloadConnection *upstream );
39
40 int
request_abandon(LloadConnection * c,LloadOperation * op)41 request_abandon( LloadConnection *c, LloadOperation *op )
42 {
43 LloadOperation *request, needle = { .o_client_connid = c->c_connid };
44 int rc = LDAP_SUCCESS;
45
46 op->o_res = LLOAD_OP_COMPLETED;
47
48 if ( ber_decode_int( &op->o_request, &needle.o_client_msgid ) ) {
49 Debug( LDAP_DEBUG_STATS, "request_abandon: "
50 "connid=%lu msgid=%d invalid integer sent in abandon request\n",
51 c->c_connid, op->o_client_msgid );
52
53 operation_unlink( op );
54 CONNECTION_LOCK_DESTROY(c);
55 return -1;
56 }
57
58 CONNECTION_LOCK(c);
59 request = ldap_tavl_find( c->c_ops, &needle, operation_client_cmp );
60 if ( !request ) {
61 Debug( LDAP_DEBUG_STATS, "request_abandon: "
62 "connid=%lu msgid=%d requests abandon of an operation "
63 "msgid=%d not being processed anymore\n",
64 c->c_connid, op->o_client_msgid, needle.o_client_msgid );
65 CONNECTION_UNLOCK(c);
66 goto done;
67 } else if ( request->o_tag == LDAP_REQ_BIND ) {
68 /* RFC 4511 states we must not allow Abandon on Binds */
69 Debug( LDAP_DEBUG_STATS, "request_abandon: "
70 "connid=%lu msgid=%d requests abandon of a bind operation "
71 "msgid=%d\n",
72 c->c_connid, op->o_client_msgid, needle.o_client_msgid );
73 CONNECTION_UNLOCK(c);
74 goto done;
75 }
76 Debug( LDAP_DEBUG_STATS, "request_abandon: "
77 "connid=%lu msgid=%d abandoning %s msgid=%d\n",
78 c->c_connid, op->o_client_msgid,
79 lload_msgtype2str( request->o_tag ), needle.o_client_msgid );
80
81 if ( c->c_state == LLOAD_C_BINDING ) {
82 assert(0);
83 }
84
85 CONNECTION_UNLOCK(c);
86 operation_abandon( request );
87
88 done:
89 operation_unlink( op );
90 return rc;
91 }
92
93 int
request_process(LloadConnection * client,LloadOperation * op)94 request_process( LloadConnection *client, LloadOperation *op )
95 {
96 BerElement *output;
97 LloadConnection *upstream;
98 ber_int_t msgid;
99 int res, rc = LDAP_SUCCESS;
100
101 upstream = backend_select( op, &res );
102 if ( !upstream ) {
103 Debug( LDAP_DEBUG_STATS, "request_process: "
104 "connid=%lu, msgid=%d no available connection found\n",
105 op->o_client_connid, op->o_client_msgid );
106
107 operation_send_reject( op, res, "no connections available", 1 );
108 goto fail;
109 }
110 CONNECTION_ASSERT_LOCKED(upstream);
111 assert_locked( &upstream->c_io_mutex );
112 op->o_upstream = upstream;
113 op->o_upstream_connid = upstream->c_connid;
114 op->o_res = LLOAD_OP_FAILED;
115
116 /* Was it unlinked in the meantime? No need to send a response since the
117 * client is dead */
118 if ( !IS_ALIVE( op, o_refcnt ) ) {
119 LloadBackend *b = upstream->c_backend;
120
121 upstream->c_n_ops_executing--;
122 checked_unlock( &upstream->c_io_mutex );
123 CONNECTION_UNLOCK(upstream);
124
125 checked_lock( &b->b_mutex );
126 b->b_n_ops_executing--;
127 checked_unlock( &b->b_mutex );
128
129 assert( !IS_ALIVE( client, c_live ) );
130 checked_lock( &op->o_link_mutex );
131 if ( op->o_upstream ) {
132 op->o_upstream = NULL;
133 }
134 checked_unlock( &op->o_link_mutex );
135 return -1;
136 }
137
138 output = upstream->c_pendingber;
139 if ( output == NULL && (output = ber_alloc()) == NULL ) {
140 LloadBackend *b = upstream->c_backend;
141
142 upstream->c_n_ops_executing--;
143 CONNECTION_UNLOCK(upstream);
144 checked_unlock( &upstream->c_io_mutex );
145
146 checked_lock( &b->b_mutex );
147 b->b_n_ops_executing--;
148 operation_update_backend_counters( op, b );
149 checked_unlock( &b->b_mutex );
150
151 Debug( LDAP_DEBUG_ANY, "request_process: "
152 "ber_alloc failed\n" );
153
154 rc = -1;
155 goto fail;
156 }
157 upstream->c_pendingber = output;
158
159 op->o_upstream_msgid = msgid = upstream->c_next_msgid++;
160 rc = ldap_tavl_insert(
161 &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error );
162 CONNECTION_UNLOCK(upstream);
163
164 Debug( LDAP_DEBUG_TRACE, "request_process: "
165 "client connid=%lu added %s msgid=%d to upstream connid=%lu as "
166 "msgid=%d\n",
167 op->o_client_connid, lload_msgtype2str( op->o_tag ),
168 op->o_client_msgid, op->o_upstream_connid, op->o_upstream_msgid );
169 assert( rc == LDAP_SUCCESS );
170
171 lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_forwarded++;
172
173 if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) &&
174 client->c_type != LLOAD_C_PRIVILEGED ) {
175 CONNECTION_LOCK(client);
176 Debug( LDAP_DEBUG_TRACE, "request_process: "
177 "proxying identity %s to upstream\n",
178 client->c_auth.bv_val );
179 ber_printf( output, "t{titOt{{sbO}" /* "}}" */, LDAP_TAG_MESSAGE,
180 LDAP_TAG_MSGID, msgid,
181 op->o_tag, &op->o_request,
182 LDAP_TAG_CONTROLS,
183 LDAP_CONTROL_PROXY_AUTHZ, 1, &client->c_auth );
184 CONNECTION_UNLOCK(client);
185
186 if ( !BER_BVISNULL( &op->o_ctrls ) ) {
187 ber_write( output, op->o_ctrls.bv_val, op->o_ctrls.bv_len, 0 );
188 }
189
190 ber_printf( output, /* "{{" */ "}}" );
191 } else {
192 ber_printf( output, "t{titOtO}", LDAP_TAG_MESSAGE,
193 LDAP_TAG_MSGID, msgid,
194 op->o_tag, &op->o_request,
195 LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
196 }
197 checked_unlock( &upstream->c_io_mutex );
198
199 connection_write_cb( -1, 0, upstream );
200 return rc;
201
202 fail:
203 if ( upstream ) {
204 CONNECTION_LOCK_DESTROY(upstream);
205
206 operation_send_reject( op, LDAP_OTHER, "internal error", 0 );
207 }
208
209 operation_unlink( op );
210 if ( rc ) {
211 CONNECTION_LOCK_DESTROY(client);
212 }
213 return rc;
214 }
215
216 int
handle_one_request(LloadConnection * c)217 handle_one_request( LloadConnection *c )
218 {
219 BerElement *ber;
220 LloadOperation *op = NULL;
221 RequestHandler handler = NULL;
222 int over_limit = 0;
223
224 ber = c->c_currentber;
225 c->c_currentber = NULL;
226
227 CONNECTION_LOCK(c);
228 op = operation_init( c, ber );
229 if ( !op ) {
230 Debug( LDAP_DEBUG_ANY, "handle_one_request: "
231 "connid=%lu, operation_init failed\n",
232 c->c_connid );
233 CONNECTION_DESTROY(c);
234 ber_free( ber, 1 );
235 return -1;
236 }
237 if ( lload_client_max_pending &&
238 c->c_n_ops_executing >= lload_client_max_pending ) {
239 over_limit = 1;
240 }
241 CONNECTION_UNLOCK(c);
242
243 switch ( op->o_tag ) {
244 case LDAP_REQ_UNBIND:
245 /* There is never a response for this operation */
246 op->o_res = LLOAD_OP_COMPLETED;
247 operation_unlink( op );
248
249 Debug( LDAP_DEBUG_STATS, "handle_one_request: "
250 "received unbind, closing client connid=%lu\n",
251 c->c_connid );
252 CONNECTION_LOCK_DESTROY(c);
253 return -1;
254 case LDAP_REQ_BIND:
255 handler = request_bind;
256 break;
257 case LDAP_REQ_ABANDON:
258 /* We can't send a response to abandon requests even if a bind is
259 * currently in progress */
260 return request_abandon( c, op );
261 case LDAP_REQ_EXTENDED:
262 default:
263 if ( c->c_state == LLOAD_C_BINDING ) {
264 operation_send_reject(
265 op, LDAP_PROTOCOL_ERROR, "bind in progress", 0 );
266 return LDAP_SUCCESS;
267 }
268 if ( over_limit ) {
269 operation_send_reject( op, LDAP_BUSY,
270 "pending operation limit reached on this connection",
271 0 );
272 return LDAP_SUCCESS;
273 }
274 if ( c->c_io_state & LLOAD_C_READ_PAUSE ) {
275 operation_send_reject( op, LDAP_BUSY,
276 "writing side backlogged, please keep reading", 0 );
277 return LDAP_SUCCESS;
278 }
279 if ( op->o_tag == LDAP_REQ_EXTENDED ) {
280 handler = request_extended;
281 } else {
282 handler = request_process;
283 }
284 break;
285 }
286
287 if ( c->c_state == LLOAD_C_CLOSING ) {
288 operation_send_reject(
289 op, LDAP_UNAVAILABLE, "connection is shutting down", 0 );
290 return LDAP_SUCCESS;
291 }
292
293 return handler( c, op );
294 }
295
296 #ifdef HAVE_TLS
297 /*
298 * The connection has a token assigned to it when the callback is set up.
299 */
300 void
client_tls_handshake_cb(evutil_socket_t s,short what,void * arg)301 client_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
302 {
303 LloadConnection *c = arg;
304 epoch_t epoch;
305 int rc = 0;
306
307 if ( what & EV_TIMEOUT ) {
308 Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
309 "connid=%lu, timeout reached, destroying\n",
310 c->c_connid );
311 goto fail;
312 }
313
314 /*
315 * In case of StartTLS, make sure we flush the response first.
316 * Also before we try to read anything from the connection, it isn't
317 * permitted to Abandon a StartTLS exop per RFC4511 anyway.
318 */
319 checked_lock( &c->c_io_mutex );
320 if ( c->c_pendingber ) {
321 checked_unlock( &c->c_io_mutex );
322 connection_write_cb( s, what, arg );
323
324 if ( !IS_ALIVE( c, c_live ) ) {
325 goto fail;
326 }
327
328 /* Do we still have data pending? If so, connection_write_cb would
329 * already have arranged the write callback to trigger again */
330 checked_lock( &c->c_io_mutex );
331 if ( c->c_pendingber ) {
332 checked_unlock( &c->c_io_mutex );
333 return;
334 }
335 }
336
337 rc = ldap_pvt_tls_accept( c->c_sb, LLOAD_TLS_CTX );
338 checked_unlock( &c->c_io_mutex );
339 if ( rc < 0 ) {
340 goto fail;
341 }
342
343 if ( rc == 0 ) {
344 struct event_base *base = event_get_base( c->c_read_event );
345
346 /*
347 * We're finished, replace the callbacks
348 *
349 * This is deadlock-safe, since both share the same base - the one
350 * that's just running us.
351 */
352 CONNECTION_LOCK(c);
353 event_del( c->c_read_event );
354 event_del( c->c_write_event );
355
356 c->c_read_timeout = NULL;
357 event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
358 connection_read_cb, c );
359 if ( IS_ALIVE( c, c_live ) ) {
360 event_add( c->c_read_event, c->c_read_timeout );
361 }
362
363 event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
364 connection_write_cb, c );
365 Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
366 "connid=%lu finished\n",
367 c->c_connid );
368
369 c->c_is_tls = LLOAD_TLS_ESTABLISHED;
370 CONNECTION_UNLOCK(c);
371 return;
372 } else if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
373 if ( IS_ALIVE( c, c_live ) ) {
374 CONNECTION_LOCK(c);
375 event_add( c->c_write_event, lload_write_timeout );
376 CONNECTION_UNLOCK(c);
377 }
378 Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
379 "connid=%lu need write rc=%d\n",
380 c->c_connid, rc );
381 }
382 return;
383
384 fail:
385 Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
386 "connid=%lu failed rc=%d\n",
387 c->c_connid, rc );
388
389 assert( c->c_ops == NULL );
390 epoch = epoch_join();
391 CONNECTION_LOCK_DESTROY(c);
392 epoch_leave( epoch );
393 }
394 #endif /* HAVE_TLS */
395
396 LloadConnection *
client_init(ber_socket_t s,const char * peername,struct event_base * base,int flags)397 client_init(
398 ber_socket_t s,
399 const char *peername,
400 struct event_base *base,
401 int flags )
402 {
403 LloadConnection *c;
404 struct event *event;
405 event_callback_fn read_cb = connection_read_cb,
406 write_cb = connection_write_cb;
407
408 if ( (c = lload_connection_init( s, peername, flags) ) == NULL ) {
409 return NULL;
410 }
411
412 {
413 ber_len_t max = sockbuf_max_incoming_client;
414 ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
415 }
416
417 c->c_state = LLOAD_C_READY;
418
419 if ( flags & CONN_IS_TLS ) {
420 #ifdef HAVE_TLS
421 int rc;
422
423 c->c_is_tls = LLOAD_LDAPS;
424
425 rc = ldap_pvt_tls_accept( c->c_sb, LLOAD_TLS_CTX );
426 if ( rc < 0 ) {
427 Debug( LDAP_DEBUG_CONNS, "client_init: "
428 "connid=%lu failed initial TLS accept rc=%d\n",
429 c->c_connid, rc );
430 CONNECTION_LOCK(c);
431 goto fail;
432 }
433
434 if ( rc ) {
435 c->c_read_timeout = lload_timeout_net;
436 read_cb = write_cb = client_tls_handshake_cb;
437 }
438 #else /* ! HAVE_TLS */
439 assert(0);
440 #endif /* ! HAVE_TLS */
441 }
442
443 event = event_new( base, s, EV_READ|EV_PERSIST, read_cb, c );
444 if ( !event ) {
445 Debug( LDAP_DEBUG_ANY, "client_init: "
446 "Read event could not be allocated\n" );
447 CONNECTION_LOCK(c);
448 goto fail;
449 }
450 c->c_read_event = event;
451
452 event = event_new( base, s, EV_WRITE, write_cb, c );
453 if ( !event ) {
454 Debug( LDAP_DEBUG_ANY, "client_init: "
455 "Write event could not be allocated\n" );
456 CONNECTION_LOCK(c);
457 goto fail;
458 }
459 c->c_write_event = event;
460
461 c->c_destroy = client_destroy;
462 c->c_unlink = client_unlink;
463 c->c_pdu_cb = handle_one_request;
464
465 CONNECTION_LOCK(c);
466 /* We only register the write event when we have data pending */
467 event_add( c->c_read_event, c->c_read_timeout );
468
469 checked_lock( &clients_mutex );
470 LDAP_CIRCLEQ_INSERT_TAIL( &clients, c, c_next );
471 checked_unlock( &clients_mutex );
472 CONNECTION_UNLOCK(c);
473
474 return c;
475 fail:
476 if ( c->c_write_event ) {
477 event_free( c->c_write_event );
478 c->c_write_event = NULL;
479 }
480 if ( c->c_read_event ) {
481 event_free( c->c_read_event );
482 c->c_read_event = NULL;
483 }
484
485 c->c_state = LLOAD_C_INVALID;
486 c->c_live--;
487 c->c_refcnt--;
488 connection_destroy( c );
489 return NULL;
490 }
491
492 void
client_reset(LloadConnection * c)493 client_reset( LloadConnection *c )
494 {
495 TAvlnode *root;
496 long freed = 0, executing;
497
498 CONNECTION_ASSERT_LOCKED(c);
499 root = c->c_ops;
500 c->c_ops = NULL;
501 executing = c->c_n_ops_executing;
502 c->c_n_ops_executing = 0;
503
504 if ( !BER_BVISNULL( &c->c_auth ) ) {
505 ch_free( c->c_auth.bv_val );
506 BER_BVZERO( &c->c_auth );
507 }
508 if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) {
509 ch_free( c->c_sasl_bind_mech.bv_val );
510 BER_BVZERO( &c->c_sasl_bind_mech );
511 }
512 CONNECTION_UNLOCK(c);
513
514 if ( root ) {
515 freed = ldap_tavl_free( root, (AVL_FREE)operation_abandon );
516 Debug( LDAP_DEBUG_TRACE, "client_reset: "
517 "dropped %ld operations\n",
518 freed );
519 }
520 assert( freed == executing );
521
522 CONNECTION_LOCK(c);
523 CONNECTION_ASSERT_LOCKED(c);
524 }
525
526 void
client_unlink(LloadConnection * c)527 client_unlink( LloadConnection *c )
528 {
529 enum sc_state state;
530 struct event *read_event, *write_event;
531
532 Debug( LDAP_DEBUG_CONNS, "client_unlink: "
533 "removing client connid=%lu\n",
534 c->c_connid );
535
536 CONNECTION_ASSERT_LOCKED(c);
537 assert( c->c_state != LLOAD_C_INVALID );
538 assert( c->c_state != LLOAD_C_DYING );
539
540 state = c->c_state;
541 c->c_state = LLOAD_C_DYING;
542
543 read_event = c->c_read_event;
544 write_event = c->c_write_event;
545 CONNECTION_UNLOCK(c);
546
547 if ( read_event ) {
548 event_del( read_event );
549 }
550
551 if ( write_event ) {
552 event_del( write_event );
553 }
554
555 if ( state != LLOAD_C_DYING ) {
556 checked_lock( &clients_mutex );
557 LDAP_CIRCLEQ_REMOVE( &clients, c, c_next );
558 checked_unlock( &clients_mutex );
559 }
560
561 CONNECTION_LOCK(c);
562 client_reset( c );
563 CONNECTION_ASSERT_LOCKED(c);
564 }
565
566 void
client_destroy(LloadConnection * c)567 client_destroy( LloadConnection *c )
568 {
569 Debug( LDAP_DEBUG_CONNS, "client_destroy: "
570 "destroying client connid=%lu\n",
571 c->c_connid );
572
573 CONNECTION_LOCK(c);
574 assert( c->c_state == LLOAD_C_DYING );
575 c->c_state = LLOAD_C_INVALID;
576
577 assert( c->c_ops == NULL );
578
579 if ( c->c_read_event ) {
580 event_free( c->c_read_event );
581 c->c_read_event = NULL;
582 }
583
584 if ( c->c_write_event ) {
585 event_free( c->c_write_event );
586 c->c_write_event = NULL;
587 }
588
589 assert( c->c_refcnt == 0 );
590 connection_destroy( c );
591 }
592
593 void
clients_destroy(int gentle)594 clients_destroy( int gentle )
595 {
596 checked_lock( &clients_mutex );
597 connections_walk(
598 &clients_mutex, &clients, lload_connection_close, &gentle );
599 checked_unlock( &clients_mutex );
600 }
601