From: Stefan Metzmacher Date: Tue, 23 Jan 2024 16:21:35 +0000 (+0100) Subject: s3:tldap: add support for [START]TLS X-Git-Tag: tdb-1.4.11~954 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=29b77a34aa85dd2b336d4f4e21088de57fc4a001;p=thirdparty%2Fsamba.git s3:tldap: add support for [START]TLS Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett --- diff --git a/source3/include/tldap.h b/source3/include/tldap.h index dbfe647a4e6..676cf44714b 100644 --- a/source3/include/tldap.h +++ b/source3/include/tldap.h @@ -124,6 +124,12 @@ bool tevent_req_is_ldap_error(struct tevent_req *req, TLDAPRC *perr); struct tldap_context *tldap_context_create(TALLOC_CTX *mem_ctx, int fd); struct tstream_context *tldap_get_plain_tstream(struct tldap_context *ld); +void tldap_set_starttls_needed(struct tldap_context *ld, bool needed); +bool tldap_get_starttls_needed(struct tldap_context *ld); +bool tldap_has_tls_tstream(struct tldap_context *ld); +const DATA_BLOB *tldap_tls_channel_bindings(struct tldap_context *ld); +void tldap_set_tls_tstream(struct tldap_context *ld, + struct tstream_context **stream); bool tldap_has_gensec_tstream(struct tldap_context *ld); void tldap_set_gensec_tstream(struct tldap_context *ld, struct tstream_context **stream); diff --git a/source3/lib/tldap.c b/source3/lib/tldap.c index 72763fb051a..ac95272fe06 100644 --- a/source3/lib/tldap.c +++ b/source3/lib/tldap.c @@ -27,6 +27,8 @@ #include "../lib/util/asn1.h" #include "../lib/tsocket/tsocket.h" #include "../lib/util/tevent_unix.h" +#include "../libcli/util/ntstatus.h" +#include "../source4/lib/tls/tls.h" static TLDAPRC tldap_simple_recv(struct tevent_req *req); static bool tldap_msg_set_pending(struct tevent_req *req); @@ -84,6 +86,8 @@ struct tldap_ctx_attribute { struct tldap_context { int ld_version; struct tstream_context *plain; + bool starttls_needed; + struct tstream_context *tls; struct tstream_context *gensec; struct tstream_context *active; int msgid; @@ -226,6 +230,48 @@ struct tstream_context *tldap_get_plain_tstream(struct tldap_context *ld) return ld->plain; } +void tldap_set_starttls_needed(struct tldap_context *ld, bool needed) +{ + if (ld == NULL) { + return; + } + + ld->starttls_needed = needed; +} + +bool tldap_get_starttls_needed(struct tldap_context *ld) +{ + if (ld == NULL) { + return false; + } + + return ld->starttls_needed; +} + +bool tldap_has_tls_tstream(struct tldap_context *ld) +{ + return ld->tls != NULL && ld->active == ld->tls; +} + +const DATA_BLOB *tldap_tls_channel_bindings(struct tldap_context *ld) +{ + return tstream_tls_channel_bindings(ld->tls); +} + +void tldap_set_tls_tstream(struct tldap_context *ld, + struct tstream_context **stream) +{ + TALLOC_FREE(ld->tls); + if (stream != NULL) { + ld->tls = talloc_move(ld, stream); + } + if (ld->tls != NULL) { + ld->active = ld->tls; + } else { + ld->active = ld->plain; + } +} + bool tldap_has_gensec_tstream(struct tldap_context *ld) { return ld->gensec != NULL && ld->active == ld->gensec; @@ -468,6 +514,7 @@ static void _tldap_context_disconnect(struct tldap_context *ld, TALLOC_FREE(ld->read_req); ld->active = NULL; TALLOC_FREE(ld->gensec); + TALLOC_FREE(ld->tls); TALLOC_FREE(ld->plain); while (talloc_array_length(ld->pending) > 0) { diff --git a/source3/lib/tldap_gensec_bind.c b/source3/lib/tldap_gensec_bind.c index 6285da2a974..5008806e654 100644 --- a/source3/lib/tldap_gensec_bind.c +++ b/source3/lib/tldap_gensec_bind.c @@ -59,6 +59,7 @@ struct tevent_req *tldap_gensec_bind_send( { struct tevent_req *req = NULL; struct tldap_gensec_bind_state *state = NULL; + const DATA_BLOB *tls_cb = NULL; NTSTATUS status; req = tevent_req_create(mem_ctx, &state, @@ -127,6 +128,37 @@ struct tevent_req *tldap_gensec_bind_send( } } + if (tldap_has_tls_tstream(state->ctx)) { + if (gensec_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) { + DBG_WARNING("sign or seal not allowed over tls\n"); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return tevent_req_post(req, ev); + } + + tls_cb = tldap_tls_channel_bindings(state->ctx); + } + + if (tls_cb != NULL) { + uint32_t initiator_addrtype = 0; + const DATA_BLOB *initiator_address = NULL; + uint32_t acceptor_addrtype = 0; + const DATA_BLOB *acceptor_address = NULL; + const DATA_BLOB *application_data = tls_cb; + + status = gensec_set_channel_bindings(state->gensec, + initiator_addrtype, + initiator_address, + acceptor_addrtype, + acceptor_address, + application_data); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("gensec_set_channel_bindings: %s\n", + nt_errstr(status)); + tevent_req_ldap_error(req, TLDAP_OPERATIONS_ERROR); + return tevent_req_post(req, ev); + } + } + gensec_want_feature(state->gensec, state->gensec_features); status = gensec_start_mech_by_sasl_name(state->gensec, "GSS-SPNEGO"); diff --git a/source3/lib/tldap_tls_connect.c b/source3/lib/tldap_tls_connect.c new file mode 100644 index 00000000000..daf33dba340 --- /dev/null +++ b/source3/lib/tldap_tls_connect.c @@ -0,0 +1,229 @@ +/* + * Unix SMB/CIFS implementation. + * tls based tldap connect + * 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 . + */ + +#include "replace.h" +#include "tldap.h" +#include "tldap_tls_connect.h" +#include "lib/util/samba_util.h" +#include "lib/util/debug.h" +#include "lib/param/param.h" +#include "../libcli/util/ntstatus.h" +#include "../source4/lib/tls/tls.h" + +struct tldap_tls_connect_state { + struct tevent_context *ev; + struct tldap_context *ctx; + struct loadparm_context *lp_ctx; + const char *peer_name; +}; + +static void tldap_tls_connect_starttls_done(struct tevent_req *subreq); +static void tldap_tls_connect_crypto_start(struct tevent_req *req); +static void tldap_tls_connect_crypto_done(struct tevent_req *subreq); + +struct tevent_req *tldap_tls_connect_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ctx, + struct loadparm_context *lp_ctx, + const char *peer_name) +{ + struct tevent_req *req = NULL; + struct tldap_tls_connect_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct tldap_tls_connect_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ctx = ctx; + state->lp_ctx = lp_ctx; + state->peer_name = peer_name; + + if (!tldap_connection_ok(ctx)) { + DBG_ERR("tldap_connection_ok() => false\n"); + tevent_req_ldap_error(req, TLDAP_CONNECT_ERROR); + return tevent_req_post(req, ev); + } + + if (tldap_has_gensec_tstream(ctx)) { + DBG_ERR("tldap_has_gensec_tstream() => true\n"); + tevent_req_ldap_error(req, TLDAP_LOCAL_ERROR); + return tevent_req_post(req, ev); + } + + if (tldap_get_starttls_needed(ctx)) { + struct tevent_req *subreq = NULL; + static const char *start_tls_oid = "1.3.6.1.4.1.1466.20037"; + + subreq = tldap_extended_send(state, + state->ev, + state->ctx, + start_tls_oid, + NULL, /* in_blob */ + NULL, /* sctrls */ + 0, /* num_sctrls */ + NULL, /* cctrls */ + 0); /* num_cctrls */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + tldap_tls_connect_starttls_done, + req); + + return req; + } + + tldap_tls_connect_crypto_start(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void tldap_tls_connect_starttls_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_tls_connect_state *state = tevent_req_data( + req, struct tldap_tls_connect_state); + TLDAPRC rc; + + rc = tldap_extended_recv(subreq, state, NULL, NULL); + TALLOC_FREE(subreq); + if (!TLDAP_RC_IS_SUCCESS(rc)) { + DBG_ERR("tldap_extended_recv(STARTTLS, %s): %s\n", + state->peer_name, tldap_rc2string(rc)); + tevent_req_ldap_error(req, rc); + return; + } + + tldap_set_starttls_needed(state->ctx, false); + + tldap_tls_connect_crypto_start(req); +} + +static void tldap_tls_connect_crypto_start(struct tevent_req *req) +{ + struct tldap_tls_connect_state *state = tevent_req_data( + req, struct tldap_tls_connect_state); + struct tstream_context *plain_stream = NULL; + struct tstream_tls_params *tls_params = NULL; + struct tevent_req *subreq = NULL; + NTSTATUS status; + + plain_stream = tldap_get_plain_tstream(state->ctx); + if (plain_stream == NULL) { + DBG_ERR("tldap_get_plain_tstream() = NULL\n"); + tevent_req_ldap_error(req, TLDAP_LOCAL_ERROR); + return; + } + + status = tstream_tls_params_client_lpcfg(state, + state->lp_ctx, + state->peer_name, + &tls_params); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("tstream_tls_params_client_lpcfg(%s): %s\n", + state->peer_name, nt_errstr(status)); + tevent_req_ldap_error(req, TLDAP_LOCAL_ERROR); + return; + } + + subreq = tstream_tls_connect_send(state, + state->ev, + plain_stream, + tls_params); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + tldap_tls_connect_crypto_done, + req); +} + +static void tldap_tls_connect_crypto_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_tls_connect_state *state = tevent_req_data( + req, struct tldap_tls_connect_state); + struct tstream_context *tls_stream = NULL; + int ret; + int error; + + ret = tstream_tls_connect_recv(subreq, &error, state, &tls_stream); + TALLOC_FREE(subreq); + if (ret != 0) { + DBG_ERR("tstream_tls_connect_recv(%s): %d %d\n", + state->peer_name, ret, error); + tevent_req_ldap_error(req, TLDAP_CONNECT_ERROR); + return; + } + + tldap_set_tls_tstream(state->ctx, &tls_stream); + + tevent_req_done(req); +} + +TLDAPRC tldap_tls_connect_recv(struct tevent_req *req) +{ + TLDAPRC rc; + + if (tevent_req_is_ldap_error(req, &rc)) { + return rc; + } + + return TLDAP_SUCCESS; +} + +TLDAPRC tldap_tls_connect( + struct tldap_context *ctx, + struct loadparm_context *lp_ctx, + const char *peer_name) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + TLDAPRC rc = TLDAP_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = tldap_tls_connect_send(frame, + ev, + ctx, + lp_ctx, + peer_name); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll(req, ev)) { + rc = TLDAP_OPERATIONS_ERROR; + goto fail; + } + rc = tldap_tls_connect_recv(req); + fail: + TALLOC_FREE(frame); + return rc; +} diff --git a/source3/lib/tldap_tls_connect.h b/source3/lib/tldap_tls_connect.h new file mode 100644 index 00000000000..6225d62e5eb --- /dev/null +++ b/source3/lib/tldap_tls_connect.h @@ -0,0 +1,39 @@ +/* + * Unix SMB/CIFS implementation. + * tls based tldap connect + * 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 . + */ + +#ifndef __TLDAP_TLS_CONNECT_H__ +#define __TLDAP_TLS_CONNECT_H__ + +struct tevent_context; +struct tldap_context; +struct loadparm_context; + +struct tevent_req *tldap_tls_connect_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ctx, + struct loadparm_context *lp_ctx, + const char *peer_name); +TLDAPRC tldap_tls_connect_recv(struct tevent_req *req); +TLDAPRC tldap_tls_connect( + struct tldap_context *ctx, + struct loadparm_context *lp_ctx, + const char *peer_name); + +#endif diff --git a/source3/wscript_build b/source3/wscript_build index 50f79c2cb29..16927880e47 100644 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -98,11 +98,13 @@ bld.SAMBA3_SUBSYSTEM('TLDAP', lib/tldap.c lib/tldap_util.c lib/tldap_gensec_bind.c + lib/tldap_tls_connect.c ''', deps=''' asn1util LIBTSOCKET samba3util + LIBTLS ''') bld.SAMBA3_BINARY('test_tldap', @@ -111,6 +113,7 @@ bld.SAMBA3_BINARY('test_tldap', asn1util LIBTSOCKET samba3util + LIBTLS smbconf cmocka ''',