s4:lib/tls: add tstream_tls_sync_setup()
authorStefan Metzmacher <metze@samba.org>
Tue, 6 Feb 2024 10:48:41 +0000 (11:48 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 23 Apr 2024 23:50:34 +0000 (23:50 +0000)
This operates in a non-async fashion and may block
in the push and pull function.

It will be used to plug into openldap transport
layer, this is needed in order to have access
to the channel bindings. And also use the same
configuration for all our gnutls based tls code.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/lib/tls/tls.h
source4/lib/tls/tls_tstream.c

index 061973cd8fa61f36c4d52d3772a9fe219773743f..66d7cb68111bfd2666cbacf02c99c040e4f32743 100644 (file)
@@ -33,6 +33,7 @@ void tls_cert_generate(TALLOC_CTX *mem_ctx,
 
 struct tstream_context;
 struct tstream_tls_params;
+struct tstream_tls_sync;
 
 enum tls_verify_peer_state {
        TLS_VERIFY_PEER_NO_CHECK = 0,
@@ -111,4 +112,22 @@ int tstream_tls_accept_recv(struct tevent_req *req,
                            TALLOC_CTX *mem_ctx,
                            struct tstream_context **tls_stream);
 
+ssize_t tstream_tls_sync_read(struct tstream_tls_sync *tlsss,
+                             void *buf, size_t len);
+ssize_t tstream_tls_sync_write(struct tstream_tls_sync *tlsss,
+                              const void *buf, size_t len);
+size_t tstream_tls_sync_pending(struct tstream_tls_sync *tlsss);
+NTSTATUS tstream_tls_sync_setup(struct tstream_tls_params *_tls_params,
+                               void *io_private,
+                               ssize_t (*io_send_fn)(void *io_private,
+                                                     const uint8_t *buf,
+                                                     size_t len),
+                               ssize_t (*io_recv_fn)(void *io_private,
+                                                     uint8_t *buf,
+                                                     size_t len),
+                               TALLOC_CTX *mem_ctx,
+                               struct tstream_tls_sync **_tlsss);
+
+const DATA_BLOB *tstream_tls_sync_channel_bindings(struct tstream_tls_sync *tlsss);
+
 #endif /* _TLS_H_ */
index 6b2c4c674c9b88fe3626d3b8fdbf74e8cf7a383e..081bddfe21b49a7da0619d0c6e9b1cf73b5458df 100644 (file)
@@ -1717,3 +1717,179 @@ int tstream_tls_accept_recv(struct tevent_req *req,
        tevent_req_received(req);
        return 0;
 }
+
+struct tstream_tls_sync {
+       struct tstream_tls *tlss;
+       void *io_private;
+       ssize_t (*io_send_fn)(void *io_private,
+                             const uint8_t *buf,
+                             size_t len);
+       ssize_t (*io_recv_fn)(void *io_private,
+                             uint8_t *buf,
+                             size_t len);
+};
+
+const DATA_BLOB *tstream_tls_sync_channel_bindings(struct tstream_tls_sync *tlsss)
+{
+       return &tlsss->tlss->channel_bindings;
+}
+
+static ssize_t tstream_tls_sync_push_function(gnutls_transport_ptr_t ptr,
+                                             const void *buf, size_t size)
+{
+       struct tstream_tls_sync *tlsss =
+               talloc_get_type_abort(ptr,
+               struct tstream_tls_sync);
+
+       return tlsss->io_send_fn(tlsss->io_private, buf, size);
+}
+
+static ssize_t tstream_tls_sync_pull_function(gnutls_transport_ptr_t ptr,
+                                             void *buf, size_t size)
+{
+       struct tstream_tls_sync *tlsss =
+               talloc_get_type_abort(ptr,
+               struct tstream_tls_sync);
+
+       return tlsss->io_recv_fn(tlsss->io_private, buf, size);
+}
+
+ssize_t tstream_tls_sync_read(struct tstream_tls_sync *tlsss,
+                             void *buf, size_t len)
+{
+       int ret;
+
+       ret = gnutls_record_recv(tlsss->tlss->tls_session, buf, len);
+       if (ret == GNUTLS_E_INTERRUPTED) {
+               errno = EINTR;
+               return -1;
+       }
+       if (ret == GNUTLS_E_AGAIN) {
+               errno = EAGAIN;
+               return -1;
+       }
+
+       if (ret < 0) {
+               DBG_WARNING("TLS gnutls_record_recv(%zu) - %s\n",
+                           (size_t)len, gnutls_strerror(ret));
+               errno = EIO;
+               return -1;
+       }
+
+       return ret;
+}
+
+ssize_t tstream_tls_sync_write(struct tstream_tls_sync *tlsss,
+                              const void *buf, size_t len)
+{
+       int ret;
+
+       ret = gnutls_record_send(tlsss->tlss->tls_session, buf, len);
+       if (ret == GNUTLS_E_INTERRUPTED) {
+               errno = EINTR;
+               return -1;
+       }
+       if (ret == GNUTLS_E_AGAIN) {
+               errno = EAGAIN;
+               return -1;
+       }
+
+       if (ret < 0) {
+               DBG_WARNING("TLS gnutls_record_send(%zu) - %s\n",
+                           (size_t)len, gnutls_strerror(ret));
+               errno = EIO;
+               return -1;
+       }
+
+       return ret;
+}
+
+size_t tstream_tls_sync_pending(struct tstream_tls_sync *tlsss)
+{
+       return gnutls_record_check_pending(tlsss->tlss->tls_session);
+}
+
+NTSTATUS tstream_tls_sync_setup(struct tstream_tls_params *_tls_params,
+                               void *io_private,
+                               ssize_t (*io_send_fn)(void *io_private,
+                                                     const uint8_t *buf,
+                                                     size_t len),
+                               ssize_t (*io_recv_fn)(void *io_private,
+                                                     uint8_t *buf,
+                                                     size_t len),
+                               TALLOC_CTX *mem_ctx,
+                               struct tstream_tls_sync **_tlsss)
+{
+       struct tstream_tls_sync *tlsss = NULL;
+       struct tstream_tls *tlss = NULL;
+       NTSTATUS status;
+       int ret;
+
+       tlsss = talloc_zero(mem_ctx, struct tstream_tls_sync);
+       if (tlsss == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       tlsss->io_private = io_private;
+       tlsss->io_send_fn = io_send_fn;
+       tlsss->io_recv_fn = io_recv_fn;
+
+       tlss = talloc_zero(tlsss, struct tstream_tls);
+       if (tlss == NULL) {
+               TALLOC_FREE(tlsss);
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_set_destructor(tlss, tstream_tls_destructor);
+       tlss->is_server = false;
+
+       tlsss->tlss = tlss;
+
+       status = tstream_tls_prepare_gnutls(_tls_params, tlss);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(tlsss);
+               return status;
+       }
+
+       gnutls_transport_set_ptr(tlss->tls_session,
+                                (gnutls_transport_ptr_t)tlsss);
+       gnutls_transport_set_pull_function(tlss->tls_session,
+                                          (gnutls_pull_func)tstream_tls_sync_pull_function);
+       gnutls_transport_set_push_function(tlss->tls_session,
+                                          (gnutls_push_func)tstream_tls_sync_push_function);
+
+       do {
+               /*
+                * The caller should have the socket blocking
+                * and do the timeout handling in the
+                * io_send/recv_fn
+                */
+               ret = gnutls_handshake(tlss->tls_session);
+       } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
+
+       if (gnutls_error_is_fatal(ret) != 0) {
+               TALLOC_FREE(tlsss);
+               return gnutls_error_to_ntstatus(ret,
+                               NT_STATUS_CRYPTO_SYSTEM_INVALID);
+       }
+
+       if (ret != GNUTLS_E_SUCCESS) {
+               TALLOC_FREE(tlsss);
+               return gnutls_error_to_ntstatus(ret,
+                               NT_STATUS_CRYPTO_SYSTEM_INVALID);
+       }
+
+       status = tstream_tls_verify_peer(tlss);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(tlsss);
+               return status;
+       }
+
+       status = tstream_tls_setup_channel_bindings(tlss);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(tlsss);
+               return status;
+       }
+
+       *_tlsss = tlsss;
+       return NT_STATUS_OK;
+}