s3:libads: add tls_wrapping into openldap
authorStefan Metzmacher <metze@samba.org>
Tue, 30 Jan 2024 09:27:58 +0000 (10:27 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 23 Apr 2024 23:50:34 +0000 (23:50 +0000)
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source3/libads/ads_proto.h
source3/libads/ldap.c
source3/libads/tls_wrapping.c [new file with mode: 0644]
source3/librpc/idl/ads.idl
source3/wscript_build

index ceefcd6d807ce9b2c62437a51e9cfba82623cebe..2fb915996a6edc769e847080268a3449234b9f51 100644 (file)
@@ -212,6 +212,16 @@ void ndr_print_ads_saslwrap_struct(struct ndr_print *ndr,
                                   const char *name,
                                   const struct ads_saslwrap *r);
 
+/* The following definitions come from libads/tls_wrapping.c  */
+
+void ndr_print_ads_tlswrap_struct(struct ndr_print *ndr,
+                                  const char *name,
+                                  const struct ads_tlswrap *r);
+ADS_STATUS ads_setup_tls_wrapping(struct ads_tlswrap *wrap,
+                                 LDAP *ld,
+                                 const char *server_name);
+const DATA_BLOB *ads_tls_channel_bindings(struct ads_tlswrap *wrap);
+
 /* The following definitions come from libads/util.c  */
 
 ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_principal);
index 1b7bdcf2d6e70b3ec44781504f0cb6c1fd8dfabc..84771f6a43e3306d3d6a0e3a73e6cc42099b1055 100644 (file)
@@ -849,6 +849,7 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads)
        }
 
        ads_zero_ldap(ads);
+       ZERO_STRUCT(ads->ldap_tls_data);
        ZERO_STRUCT(ads->ldap_wrap_data);
        ads->ldap.last_attempt  = time_mono(NULL);
        ads->ldap_wrap_data.wrap_type   = ADS_SASLWRAP_TYPE_PLAIN;
@@ -967,6 +968,12 @@ got_connection:
                goto out;
        }
 
+       ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
+       if (!ads->ldap_tls_data.mem_ctx) {
+               status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+               goto out;
+       }
+
        ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
        if (!ads->ldap_wrap_data.mem_ctx) {
                status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
@@ -1061,6 +1068,9 @@ void ads_disconnect(ADS_STRUCT *ads)
                ldap_unbind(ads->ldap.ld);
                ads->ldap.ld = NULL;
        }
+       if (ads->ldap_tls_data.mem_ctx) {
+               talloc_free(ads->ldap_tls_data.mem_ctx);
+       }
        if (ads->ldap_wrap_data.wrap_ops &&
                ads->ldap_wrap_data.wrap_ops->disconnect) {
                ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
@@ -1069,6 +1079,7 @@ void ads_disconnect(ADS_STRUCT *ads)
                talloc_free(ads->ldap_wrap_data.mem_ctx);
        }
        ads_zero_ldap(ads);
+       ZERO_STRUCT(ads->ldap_tls_data);
        ZERO_STRUCT(ads->ldap_wrap_data);
 }
 
diff --git a/source3/libads/tls_wrapping.c b/source3/libads/tls_wrapping.c
new file mode 100644 (file)
index 0000000..c25ab67
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+   Unix SMB/CIFS implementation.
+   ads tls wrapping code
+   Copyright (C) Stefan Metzmacher 2024
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ads.h"
+#include "lib/param/param.h"
+#include "../source4/lib/tls/tls.h"
+
+void ndr_print_ads_tlswrap_struct(struct ndr_print *ndr, const char *name, const struct ads_tlswrap *r)
+{
+       ndr_print_struct(ndr, name, "tlswrap");
+       ndr->depth++;
+       ndr_print_ptr(ndr, "mem_ctx", r->mem_ctx);
+       ndr_print_timeval(ndr, "endtime", &r->endtime);
+#ifdef HAVE_ADS
+       ndr_print_ptr(ndr, "sbiod", r->sbiod);
+       ndr_print_ptr(ndr, "tls_params", r->tls_params);
+       ndr_print_ptr(ndr, "tls_sync", r->tls_sync);
+#endif /* HAVE_ADS */
+       ndr->depth--;
+}
+
+#ifdef HAVE_ADS
+
+static int ads_tlswrap_setup(Sockbuf_IO_Desc *sbiod, void *arg)
+{
+       struct ads_tlswrap *wrap = (struct ads_tlswrap *)arg;
+
+       wrap->sbiod = sbiod;
+
+       sbiod->sbiod_pvt = wrap;
+
+       return 0;
+}
+
+static int ads_tlswrap_remove(Sockbuf_IO_Desc *sbiod)
+{
+       struct ads_tlswrap *wrap =
+                       (struct ads_tlswrap *)sbiod->sbiod_pvt;
+
+       wrap->sbiod = NULL;
+
+       return 0;
+}
+
+static ssize_t ads_tlswrap_send_function(gnutls_transport_ptr_t ptr,
+                                        const uint8_t *buf, size_t size)
+{
+       struct ads_tlswrap *wrap = (struct ads_tlswrap *)ptr;
+
+       if (wrap->endtime.tv_sec != 0) {
+               if (timeval_expired(&wrap->endtime)) {
+                       errno = ECONNRESET;
+                       return -1;
+               }
+       }
+
+       return LBER_SBIOD_WRITE_NEXT(wrap->sbiod, discard_const(buf), size);
+}
+
+static ssize_t ads_tlswrap_recv_function(gnutls_transport_ptr_t ptr,
+                                        uint8_t *buf, size_t size)
+{
+       struct ads_tlswrap *wrap = (struct ads_tlswrap *)ptr;
+
+       if (wrap->endtime.tv_sec != 0) {
+               if (timeval_expired(&wrap->endtime)) {
+                       errno = ECONNRESET;
+                       return -1;
+               }
+       }
+
+       return LBER_SBIOD_READ_NEXT(wrap->sbiod, buf, size);
+}
+
+static ber_slen_t ads_tlswrap_read(Sockbuf_IO_Desc *sbiod,
+                                  void *buf, ber_len_t len)
+{
+       struct ads_tlswrap *wrap =
+                       (struct ads_tlswrap *)sbiod->sbiod_pvt;
+
+       return tstream_tls_sync_read(wrap->tls_sync, buf, len);
+}
+
+static ber_slen_t ads_tlswrap_write(Sockbuf_IO_Desc *sbiod,
+                                   void *buf, ber_len_t len)
+{
+       struct ads_tlswrap *wrap =
+                       (struct ads_tlswrap *)sbiod->sbiod_pvt;
+
+       return tstream_tls_sync_write(wrap->tls_sync, buf, len);
+}
+
+static int ads_tlswrap_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
+{
+       struct ads_tlswrap *wrap =
+                       (struct ads_tlswrap *)sbiod->sbiod_pvt;
+       int ret;
+
+       switch (opt) {
+       case LBER_SB_OPT_DATA_READY:
+               if (tstream_tls_sync_pending(wrap->tls_sync) > 0) {
+                       return 1;
+               }
+
+               ret = LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg);
+               break;
+       default:
+               ret = LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg);
+               break;
+       }
+
+       return ret;
+}
+
+static int ads_tlswrap_close(Sockbuf_IO_Desc *sbiod)
+{
+       struct ads_tlswrap *wrap =
+                       (struct ads_tlswrap *)sbiod->sbiod_pvt;
+
+       TALLOC_FREE(wrap->tls_sync);
+       TALLOC_FREE(wrap->tls_params);
+
+       return 0;
+}
+
+static const Sockbuf_IO ads_tlswrap_sockbuf_io = {
+       ads_tlswrap_setup,      /* sbi_setup */
+       ads_tlswrap_remove,     /* sbi_remove */
+       ads_tlswrap_ctrl,       /* sbi_ctrl */
+       ads_tlswrap_read,       /* sbi_read */
+       ads_tlswrap_write,      /* sbi_write */
+       ads_tlswrap_close       /* sbi_close */
+};
+
+ADS_STATUS ads_setup_tls_wrapping(struct ads_tlswrap *wrap,
+                                 LDAP *ld,
+                                 const char *server_name)
+{
+       Sockbuf_IO *io = discard_const_p(Sockbuf_IO, &ads_tlswrap_sockbuf_io);
+       Sockbuf *sb = NULL;
+       struct loadparm_context *lp_ctx = NULL;
+       ADS_STATUS status;
+       NTSTATUS ntstatus;
+       unsigned to;
+       int rc;
+
+       rc = ldap_get_option(ld, LDAP_OPT_SOCKBUF, &sb);
+       status = ADS_ERROR_LDAP(rc);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       lp_ctx = loadparm_init_s3(talloc_tos(), loadparm_s3_helpers());
+       if (lp_ctx == NULL) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       ntstatus = tstream_tls_params_client_lpcfg(wrap->mem_ctx,
+                                                  lp_ctx,
+                                                  server_name,
+                                                  &wrap->tls_params);
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               return ADS_ERROR_NT(ntstatus);
+       }
+
+       /* setup the real wrapping callbacks */
+       rc = ber_sockbuf_add_io(sb, io, LBER_SBIOD_LEVEL_TRANSPORT, wrap);
+       status = ADS_ERROR_LDAP(rc);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       to = lpcfg_ldap_connection_timeout(lp_ctx);
+       wrap->endtime = timeval_current_ofs(to, 0);
+       ntstatus = tstream_tls_sync_setup(wrap->tls_params,
+                                         wrap,
+                                         ads_tlswrap_send_function,
+                                         ads_tlswrap_recv_function,
+                                         wrap->mem_ctx,
+                                         &wrap->tls_sync);
+       wrap->endtime = timeval_zero();
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               ber_sockbuf_remove_io(sb, io, LBER_SBIOD_LEVEL_TRANSPORT);
+               return ADS_ERROR_NT(ntstatus);
+       }
+
+       return ADS_SUCCESS;
+}
+
+const DATA_BLOB *ads_tls_channel_bindings(struct ads_tlswrap *wrap)
+{
+       if (wrap->tls_sync == NULL) {
+               return NULL;
+       }
+
+       return tstream_tls_sync_channel_bindings(wrap->tls_sync);
+}
+#else
+ADS_STATUS ads_setup_tls_wrapping(struct ads_tlswrap *wrap,
+                                 LDAP *ld,
+                                 const char *server_name)
+{
+       return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+}
+const DATA_BLOB *ads_tls_channel_bindings(struct ads_tlswrap *wrap)
+{
+       return NULL;
+}
+#endif /* HAVE_ADS */
index 4a63ace7c23280323bca8eb19b358bf076e70ea1..a706d355a5bcceb9fc542a56816b2dc0cd66af1d 100644 (file)
@@ -101,6 +101,16 @@ interface ads
                ads_ldap_buf_out out;
        } ads_saslwrap;
 
+       typedef [nopull,nopush] struct {
+               [ignore] TALLOC_CTX *mem_ctx;
+               [ignore] timeval endtime;
+#ifdef HAVE_ADS
+               [ignore] struct sockbuf_io_desc *sbiod; /* lowlevel state for LDAP wrapping */
+               [ignore] struct tstream_tls_params *tls_params;
+               [ignore] struct tstream_tls_sync *tls_sync;
+#endif /* HAVE_ADS */
+       } ads_tlswrap;
+
        typedef [nopull,nopush] struct {
                [ignore] ldap *ld;
                sockaddr_storage ss; /* the ip of the active connection, if any */
@@ -120,6 +130,7 @@ interface ads
 
 #ifdef HAVE_ADS
                ads_saslwrap ldap_wrap_data;
+               ads_tlswrap ldap_tls_data;
                /* info about the current LDAP connection */
                ads_ldap ldap;
 #endif /* HAVE_ADS */
index 16927880e47939d1598a15595440ad7c5f3a9a7d..8bb7f4e26d98f4ffdcc3bea0bb064f192047d24d 100644 (file)
@@ -497,6 +497,7 @@ bld.SAMBA3_LIBRARY('ads',
                           libads/ldap.c
                           libads/sasl.c
                           libads/sasl_wrapping.c
+                          libads/tls_wrapping.c
                           libads/krb5_setpw.c
                           libads/kerberos_util.c
                           libads/ldap_user.c