Client TLS support

This commit is contained in:
Ondřej Kuzník 2017-09-25 11:17:04 +01:00 committed by Ondřej Kuzník
parent a0cd41ecd2
commit 1b46f86627
3 changed files with 192 additions and 1 deletions

View File

@ -240,6 +240,99 @@ handle_one_request( Connection *c )
return handler( c, op );
}
/*
* The connection has a token assigned to it when the callback is set up.
*/
void
client_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
{
Connection *c = arg;
int rc = 0;
CONNECTION_LOCK_DECREF(c);
if ( what & EV_TIMEOUT ) {
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
"connid=%lu, timeout reached, destroying\n",
c->c_connid );
goto fail;
}
/*
* In case of StartTLS, make sure we flush the response first.
* Also before we try to read anything from the connection, it isn't
* permitted to Abandon a StartTLS exop per RFC4511 anyway.
*/
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
if ( c->c_pendingber ) {
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
CONNECTION_UNLOCK_INCREF(c);
connection_write_cb( s, what, arg );
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
CONNECTION_LOCK_DECREF(c);
if ( !c->c_live ) {
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
goto fail;
}
/* Do we still have data pending? If so, connection_write_cb would
* already have arranged the write callback to trigger again */
if ( c->c_pendingber ) {
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
CONNECTION_UNLOCK_INCREF(c);
return;
}
}
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
rc = ldap_pvt_tls_accept( c->c_sb, slap_tls_ctx );
if ( rc < 0 ) {
goto fail;
}
if ( rc == 0 ) {
struct event_base *base = event_get_base( c->c_read_event );
/*
* We're finished, replace the callbacks
*
* This is deadlock-safe, since both share the same base - the one
* that's just running us.
*/
event_del( c->c_read_event );
event_del( c->c_write_event );
event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
connection_read_cb, c );
event_add( c->c_read_event, NULL );
event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
connection_write_cb, c );
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
"connid=%lu finished\n",
c->c_connid );
c->c_is_tls = LLOAD_TLS_ESTABLISHED;
/* The temporary reference established for us is no longer needed */
CONNECTION_UNLOCK_OR_DESTROY(c);
return;
} else if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
event_add( c->c_write_event, lload_write_timeout );
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
"connid=%lu need write rc=%d\n",
c->c_connid, rc );
}
CONNECTION_UNLOCK_INCREF(c);
return;
fail:
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
"connid=%lu failed rc=%d\n",
c->c_connid, rc );
CONNECTION_DESTROY(c);
}
Connection *
client_init(
ber_socket_t s,
@ -266,6 +359,25 @@ client_init(
c->c_state = LLOAD_C_READY;
if ( flags & CONN_IS_TLS ) {
int rc;
c->c_is_tls = LLOAD_LDAPS;
rc = ldap_pvt_tls_accept( c->c_sb, slap_tls_ctx );
if ( rc < 0 ) {
Debug( LDAP_DEBUG_CONNS, "client_init: "
"connid=%lu failed initial TLS accept rc=%d\n",
c->c_connid, rc );
goto fail;
}
if ( rc ) {
c->c_refcnt++;
read_cb = write_cb = client_tls_handshake_cb;
}
}
event = event_new( base, s, EV_READ|EV_PERSIST, read_cb, c );
if ( !event ) {
Debug( LDAP_DEBUG_ANY, "client_init: "

View File

@ -22,6 +22,82 @@
Avlnode *lload_exop_handlers = NULL;
int
handle_starttls( Connection *c, Operation *op )
{
struct event_base *base = event_get_base( c->c_read_event );
BerElement *output;
char *msg = NULL;
int rc = LDAP_SUCCESS;
tavl_delete( &c->c_ops, op, operation_client_cmp );
if ( c->c_is_tls == LLOAD_TLS_ESTABLISHED ) {
rc = LDAP_OPERATIONS_ERROR;
msg = "TLS layer already in effect";
} else if ( c->c_state == LLOAD_C_BINDING ) {
rc = LDAP_OPERATIONS_ERROR;
msg = "bind in progress";
} else if ( c->c_ops ) {
rc = LDAP_OPERATIONS_ERROR;
msg = "cannot start TLS when operations are outstanding";
} else if ( !slap_tls_ctx ) {
rc = LDAP_UNAVAILABLE;
msg = "Could not initialize TLS";
}
Debug( LDAP_DEBUG_STATS, "handle_starttls: "
"handling StartTLS exop connid=%lu rc=%d msg=%s\n",
c->c_connid, rc, msg );
if ( rc ) {
/* We've already removed the operation from the queue */
return operation_send_reject_locked( op, rc, msg, 1 );
}
CONNECTION_UNLOCK_INCREF(c);
event_del( c->c_read_event );
event_del( c->c_write_event );
/*
* At this point, we are the only thread handling the connection:
* - there are no upstream operations
* - the I/O callbacks have been successfully removed
*
* This means we can safely reconfigure both I/O events now.
*/
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
output = c->c_pendingber;
if ( output == NULL && (output = ber_alloc()) == NULL ) {
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
CONNECTION_LOCK_DECREF(c);
operation_destroy_from_client( op );
CONNECTION_DESTROY(c);
return -1;
}
c->c_pendingber = output;
ber_printf( output, "t{tit{ess}}", LDAP_TAG_MESSAGE,
LDAP_TAG_MSGID, op->o_client_msgid,
LDAP_RES_EXTENDED, LDAP_SUCCESS, "", "" );
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
CONNECTION_LOCK_DECREF(c);
event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
client_tls_handshake_cb, c );
event_add( c->c_read_event, NULL );
event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
client_tls_handshake_cb, c );
/* We already have something to write */
event_add( c->c_write_event, lload_write_timeout );
operation_destroy_from_client( op );
CONNECTION_UNLOCK_INCREF(c);
return -1;
}
int
request_extended( Connection *c, Operation *op )
{
@ -67,7 +143,9 @@ request_extended( Connection *c, Operation *op )
return request_process( c, op );
}
ExopHandler lload_exops[] = { { BER_BVNULL }
ExopHandler lload_exops[] = {
{ BER_BVC(LDAP_EXOP_START_TLS), handle_starttls },
{ BER_BVNULL }
};
int

View File

@ -74,6 +74,7 @@ LDAP_SLAPD_F (int) handle_vc_bind_response( Operation *op, BerElement *ber );
LDAP_SLAPD_F (int) request_abandon( Connection *c, Operation *op );
LDAP_SLAPD_F (int) request_process( Connection *c, Operation *op );
LDAP_SLAPD_F (int) handle_one_request( Connection *c );
LDAP_SLAPD_F (void) client_tls_handshake_cb( evutil_socket_t s, short what, void *arg );
LDAP_SLAPD_F (Connection *) client_init( ber_socket_t s, Listener *url, const char *peername, struct event_base *base, int use_tls );
LDAP_SLAPD_F (void) client_reset( Connection *c );
LDAP_SLAPD_F (void) client_destroy( Connection *c );