]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
Upstream TLS support
authorOndřej Kuzník <ondra@mistotebe.net>
Mon, 25 Sep 2017 10:15:59 +0000 (11:15 +0100)
committerOndřej Kuzník <okuznik@symas.com>
Tue, 17 Nov 2020 17:58:14 +0000 (17:58 +0000)
servers/lloadd/config.c
servers/lloadd/connection.c
servers/lloadd/slap.h
servers/lloadd/upstream.c

index 1417368c3594c2f021a227b2840abe177fcb4a77..a0bdc9853c38f903d8bee6d5de53454951bf2515 100644 (file)
@@ -569,13 +569,14 @@ config_backend( ConfigArgs *c )
     }
 
 #else /* HAVE_TLS */
+    /* Specifying ldaps:// overrides starttls= settings */
     tmp = ldap_pvt_url_scheme2tls( lud->lud_scheme );
     if ( tmp ) {
         b->b_tls = LLOAD_LDAPS;
     }
 
     if ( !lud->lud_port ) {
-        b->b_port = b->b_tls ? LDAPS_PORT : LDAP_PORT;
+        b->b_port = tmp ? LDAPS_PORT : LDAP_PORT;
     } else {
         b->b_port = lud->lud_port;
     }
@@ -1829,9 +1830,9 @@ config_push_cleanup( ConfigArgs *ca, ConfigDriver *cleanup )
 }
 
 static slap_verbmasks tlskey[] = {
-    { BER_BVC("no"), SB_TLS_OFF },
-    { BER_BVC("yes"), SB_TLS_ON },
-    { BER_BVC("critical"), SB_TLS_CRITICAL },
+    { BER_BVC("no"), LLOAD_CLEARTEXT },
+    { BER_BVC("yes"), LLOAD_STARTTLS_OPTIONAL },
+    { BER_BVC("critical"), LLOAD_STARTTLS },
     { BER_BVNULL, 0 }
 };
 
@@ -1955,6 +1956,7 @@ static slap_cf_aux_table backendkey[] = {
 
     { BER_BVC("max-pending-ops="), offsetof(Backend, b_max_pending), 'i', 0, NULL },
     { BER_BVC("conn-max-pending="), offsetof(Backend, b_max_conn_pending), 'i', 0, NULL },
+    { BER_BVC("starttls="), offsetof(Backend, b_tls), 'i', 0, tlskey },
     { BER_BVNULL, 0, 0, 0, NULL }
 };
 
@@ -1971,7 +1973,6 @@ static slap_cf_aux_table bindkey[] = {
     { BER_BVC("authzID="), offsetof(slap_bindconf, sb_authzId), 'b', 1, NULL },
     { BER_BVC("keepalive="), offsetof(slap_bindconf, sb_keepalive), 'x', 0, (slap_verbmasks *)slap_keepalive_parse },
 #ifdef HAVE_TLS
-    { BER_BVC("starttls="), offsetof(slap_bindconf, sb_tls), 'i', 0, tlskey },
     { BER_BVC("tls_cert="), offsetof(slap_bindconf, sb_tls_cert), 's', 1, NULL },
     { BER_BVC("tls_key="), offsetof(slap_bindconf, sb_tls_key), 's', 1, NULL },
     { BER_BVC("tls_cacert="), offsetof(slap_bindconf, sb_tls_cacert), 's', 1, NULL },
index 2b1a43326e4f15b979dbbea07cd0cc9070fe9ac3..bacb93f5ebc29adb347249aa37264f9296e70428 100644 (file)
@@ -335,10 +335,6 @@ connection_init( ber_socket_t s, const char *peername, int flags )
 
     assert( peername != NULL );
 
-#ifndef HAVE_TLS
-    assert( !(flags & CONN_IS_TLS) );
-#endif
-
     if ( s == AC_SOCKET_INVALID ) {
         Debug( LDAP_DEBUG_ANY, "connection_init: "
                 "init of socket fd=%ld invalid\n",
@@ -362,10 +358,6 @@ connection_init( ber_socket_t s, const char *peername, int flags )
 #endif
         ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_fd,
                 LBER_SBIOD_LEVEL_PROVIDER, (void *)&s );
-#ifdef LDAP_PF_LOCAL_SENDMSG
-        if ( !BER_BVISEMPTY( peerbv ) )
-            ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_UNGET_BUF, peerbv );
-#endif
     } else
 #endif /* LDAP_PF_LOCAL */
     {
@@ -382,18 +374,6 @@ connection_init( ber_socket_t s, const char *peername, int flags )
             c->c_sb, &ber_sockbuf_io_debug, INT_MAX, (void *)"lload_" );
 #endif
 
-#ifdef HAVE_TLS
-    if ( flags & CONN_IS_TLS ) {
-        /* TODO: will need an asynchronous TLS implementation in libldap */
-        assert(0);
-        c->c_is_tls = 1;
-        c->c_needs_tls_accept = 1;
-    } else {
-        c->c_is_tls = 0;
-        c->c_needs_tls_accept = 0;
-    }
-#endif
-
     c->c_next_msgid = 1;
     c->c_refcnt = c->c_live = 1;
     c->c_destroy = connection_destroy;
index 396458e2cb89c760cd5061943e61d253a4c19a6b..f131687abb8c640512ff66002a93b8cee3fac797 100644 (file)
@@ -244,7 +244,9 @@ typedef enum {
 enum lload_tls_type {
     LLOAD_CLEARTEXT = 0,
     LLOAD_LDAPS,
+    LLOAD_STARTTLS_OPTIONAL,
     LLOAD_STARTTLS,
+    LLOAD_TLS_ESTABLISHED,
 };
 
 struct PendingConnection {
@@ -405,7 +407,6 @@ struct Connection {
 
 #ifdef HAVE_TLS
     enum lload_tls_type c_is_tls; /* true if this LDAP over raw TLS */
-    char c_needs_tls_accept;      /* true if SSL_accept should be called */
 #endif
 
     long c_n_ops_executing; /* num of ops currently executing */
index c64f6258d983f0e4034964871426c8b49f5b159a..49ae368fa5ff54ee055a74e3cd86566bd95f83a1 100644 (file)
@@ -426,6 +426,180 @@ upstream_finish( Connection *c )
     return rc;
 }
 
+static void
+upstream_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
+{
+    Connection *c = arg;
+    Backend *b;
+    int rc = LDAP_SUCCESS;
+
+    CONNECTION_LOCK(c);
+    if ( what & EV_TIMEOUT ) {
+        Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
+                "connid=%lu, timeout reached, destroying\n",
+                c->c_connid );
+        goto fail;
+    }
+    b = c->c_private;
+
+    rc = ldap_pvt_tls_connect( slap_tls_ld, c->c_sb, b->b_host );
+    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, "upstream_tls_handshake_cb: "
+                "connid=%lu finished\n",
+                c->c_connid );
+        c->c_is_tls = LLOAD_TLS_ESTABLISHED;
+
+        CONNECTION_UNLOCK_INCREF(c);
+        ldap_pvt_thread_mutex_lock( &b->b_mutex );
+        CONNECTION_LOCK_DECREF(c);
+
+        rc = upstream_finish( c );
+
+        ldap_pvt_thread_mutex_unlock( &b->b_mutex );
+
+        if ( rc == LDAP_SUCCESS ) {
+            backend_retry( b );
+        }
+    } 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, "upstream_tls_handshake_cb: "
+                "connid=%lu need write rc=%d\n",
+                c->c_connid, rc );
+    }
+    CONNECTION_UNLOCK_OR_DESTROY(c);
+    return;
+
+fail:
+    Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
+            "connid=%lu failed rc=%d\n",
+            c->c_connid, rc );
+    CONNECTION_DESTROY(c);
+}
+
+static int
+upstream_starttls( Connection *c )
+{
+    BerValue matcheddn, message, responseOid,
+             startTLSOid = BER_BVC(LDAP_EXOP_START_TLS);
+    BerElement *ber = c->c_currentber;
+    struct event_base *base;
+    ber_int_t msgid, result;
+    ber_tag_t tag;
+
+    c->c_currentber = NULL;
+
+    if ( ber_scanf( ber, "it", &msgid, &tag ) == LBER_ERROR ) {
+        Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+                "protocol violation from server\n" );
+        goto fail;
+    }
+
+    if ( msgid != ( c->c_next_msgid - 1 ) || tag != LDAP_RES_EXTENDED ) {
+        Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+                "unexpected %s from server, msgid=%d\n",
+                slap_msgtype2str( tag ), msgid );
+        goto fail;
+    }
+
+    if ( ber_scanf( ber, "{emm}", &result, &matcheddn, &message ) ==
+                 LBER_ERROR ) {
+        Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+                "protocol violation on StartTLS response\n" );
+        goto fail;
+    }
+
+    if ( (tag = ber_get_tag( ber )) != LBER_DEFAULT ) {
+        if ( tag != LDAP_TAG_EXOP_RES_OID ||
+                ber_scanf( ber, "{m}", &responseOid ) == LBER_DEFAULT ) {
+            Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+                    "protocol violation on StartTLS response\n" );
+            goto fail;
+        }
+
+        if ( ber_bvcmp( &responseOid, &startTLSOid ) ) {
+            Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+                    "oid=%s not a StartTLS response\n",
+                    responseOid.bv_val );
+            goto fail;
+        }
+    }
+
+    if ( result != LDAP_SUCCESS ) {
+        Backend *b = c->c_private;
+        int rc;
+
+        Debug( LDAP_DEBUG_STATS, "upstream_starttls: "
+                "server doesn't support StartTLS rc=%d message='%s'%s\n",
+                result, message.bv_val,
+                (c->c_is_tls == LLOAD_STARTTLS_OPTIONAL) ? ", ignored" : "" );
+        if ( c->c_is_tls != LLOAD_STARTTLS_OPTIONAL ) {
+            goto fail;
+        }
+        c->c_is_tls = LLOAD_CLEARTEXT;
+
+        ber_free( ber, 1 );
+
+        CONNECTION_UNLOCK_INCREF(c);
+        ldap_pvt_thread_mutex_lock( &b->b_mutex );
+        CONNECTION_LOCK_DECREF(c);
+
+        rc = upstream_finish( c );
+
+        ldap_pvt_thread_mutex_unlock( &b->b_mutex );
+
+        if ( rc == LDAP_SUCCESS ) {
+            backend_retry( b );
+        }
+
+        CONNECTION_UNLOCK_OR_DESTROY(c);
+        return rc;
+    }
+
+    base = event_get_base( c->c_read_event );
+
+    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,
+            upstream_tls_handshake_cb, c );
+    event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
+            upstream_tls_handshake_cb, c );
+
+    event_add( c->c_read_event, NULL );
+    event_add( c->c_write_event, lload_write_timeout );
+
+    CONNECTION_UNLOCK(c);
+
+    ber_free( ber, 1 );
+    return -1;
+
+fail:
+    ber_free( ber, 1 );
+    CONNECTION_DESTROY(c);
+    return -1;
+}
+
 /*
  * We must already hold b->b_mutex when called.
  */
@@ -439,7 +613,7 @@ upstream_init( ber_socket_t s, Backend *b )
 
     assert( b != NULL );
 
-    flags = (b->b_tls == LLOAD_LDAPS) ? CONN_IS_TLS : 0;
+    flags = (b->b_proto == LDAP_PROTO_IPC) ? CONN_IS_IPC : 0;
     if ( (c = connection_init( s, b->b_host, flags )) == NULL ) {
         return NULL;
     }
@@ -473,11 +647,37 @@ upstream_init( ber_socket_t s, Backend *b )
     /* We only add the write event when we have data pending */
     c->c_write_event = event;
 
-    rc = upstream_finish( c );
-    if ( rc < 0 ) {
-        goto fail;
-    }
+    if ( c->c_is_tls == LLOAD_CLEARTEXT ) {
+        rc = upstream_finish( c );
+        if ( rc < 0 ) {
+            goto fail;
+        }
+    } else if ( c->c_is_tls == LLOAD_LDAPS ) {
+        event_assign( c->c_read_event, base, s, EV_READ|EV_PERSIST,
+                upstream_tls_handshake_cb, c );
+        event_assign( c->c_write_event, base, s, EV_WRITE,
+                upstream_tls_handshake_cb, c );
+        event_add( c->c_write_event, lload_write_timeout );
+    } else if ( c->c_is_tls == LLOAD_STARTTLS ||
+            c->c_is_tls == LLOAD_STARTTLS_OPTIONAL ) {
+        BerElement *output;
+
+        ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
+        if ( (output = c->c_pendingber = ber_alloc()) == NULL ) {
+            ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
+            goto fail;
+        }
+        ber_printf( output, "t{tit{ts}}", LDAP_TAG_MESSAGE,
+                LDAP_TAG_MSGID, c->c_next_msgid++,
+                LDAP_REQ_EXTENDED,
+                LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_START_TLS );
+        ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
 
+        c->c_pdu_cb = upstream_starttls;
+        CONNECTION_UNLOCK_INCREF(c);
+        connection_write_cb( s, 0, c );
+        CONNECTION_LOCK_DECREF(c);
+    }
     event_add( c->c_read_event, NULL );
 
     c->c_destroy = upstream_destroy;