412 lines
11 KiB
Diff
412 lines
11 KiB
Diff
From f3957ca5ce206e1224874e6495780b5130d6de0c Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 11 Sep 2020 12:16:00 +0200
|
|
Subject: [PATCH 011/266] CVE-2020-25717 winbindd: add generic
|
|
wb_parent_idmap_setup_send/recv() helpers
|
|
|
|
This is more or less a copy of wb_xids2sids_init_dom_maps_send/recv,
|
|
but it's more generic and doesn't imply global state.
|
|
|
|
It also closes a initialization race by using a tevent_queue to
|
|
serialize the calls.
|
|
|
|
In the next commits we'll replace wb_xids2sids_init_dom_maps_send/recv.
|
|
|
|
We'll also use the new function in the wb_sids2xids code.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14539
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556
|
|
|
|
(cherry picked from commit 209e81a2ea8c972ee57e2f0c9579da843c0e2ac7)
|
|
---
|
|
source3/winbindd/winbindd.h | 13 ++
|
|
source3/winbindd/winbindd_idmap.c | 314 ++++++++++++++++++++++++++++++
|
|
source3/winbindd/winbindd_proto.h | 5 +
|
|
3 files changed, 332 insertions(+)
|
|
|
|
Conflict:bool is_idmap_child(const struct winbindd_child *child); --> struct dcerpc_binding_handle *idmap_child_handle(void);
|
|
Reference:https://git.samba.org/samba.git/?p=samba.git;a=patch;h=f3957ca5ce206e1224874e6495780b5130d6de0c
|
|
|
|
diff --git a/source3/winbindd/winbindd.h b/source3/winbindd/winbindd.h
|
|
index a72d6aa7830..480ba4f1282 100644
|
|
--- a/source3/winbindd/winbindd.h
|
|
+++ b/source3/winbindd/winbindd.h
|
|
@@ -189,6 +189,19 @@ struct winbindd_domain {
|
|
struct winbindd_domain *prev, *next;
|
|
};
|
|
|
|
+struct wb_parent_idmap_config_dom {
|
|
+ unsigned low_id;
|
|
+ unsigned high_id;
|
|
+ const char *name;
|
|
+ struct dom_sid sid;
|
|
+};
|
|
+
|
|
+struct wb_parent_idmap_config {
|
|
+ struct tevent_queue *queue;
|
|
+ uint32_t num_doms;
|
|
+ struct wb_parent_idmap_config_dom *doms;
|
|
+};
|
|
+
|
|
struct wb_acct_info {
|
|
const char *acct_name; /* account name */
|
|
const char *acct_desc; /* account name */
|
|
diff --git a/source3/winbindd/winbindd_idmap.c b/source3/winbindd/winbindd_idmap.c
|
|
index bd5f3a67aad..487f27fd94d 100644
|
|
--- a/source3/winbindd/winbindd_idmap.c
|
|
+++ b/source3/winbindd/winbindd_idmap.c
|
|
@@ -23,12 +23,21 @@
|
|
|
|
#include "includes.h"
|
|
#include "winbindd.h"
|
|
+#include "../libcli/security/security.h"
|
|
+#include "passdb/lookup_sid.h"
|
|
|
|
#undef DBGC_CLASS
|
|
#define DBGC_CLASS DBGC_WINBIND
|
|
|
|
static struct winbindd_child static_idmap_child;
|
|
|
|
+/*
|
|
+ * Map idmap ranges to domain names, taken from smb.conf. This is
|
|
+ * stored in the parent winbind and used to assemble xids2sids/sids2xids calls
|
|
+ * into per-idmap-domain chunks.
|
|
+ */
|
|
+static struct wb_parent_idmap_config static_parent_idmap_config;
|
|
+
|
|
struct winbindd_child *idmap_child(void)
|
|
{
|
|
return &static_idmap_child;
|
|
@@ -73,3 +82,308 @@ void init_idmap_child(void)
|
|
idmap_dispatch_table,
|
|
"log.winbindd", "idmap");
|
|
}
|
|
+
|
|
+struct wb_parent_idmap_setup_state {
|
|
+ struct tevent_context *ev;
|
|
+ struct wb_parent_idmap_config *cfg;
|
|
+ size_t dom_idx;
|
|
+};
|
|
+
|
|
+static void wb_parent_idmap_setup_cleanup(struct tevent_req *req,
|
|
+ enum tevent_req_state req_state)
|
|
+{
|
|
+ struct wb_parent_idmap_setup_state *state =
|
|
+ tevent_req_data(req,
|
|
+ struct wb_parent_idmap_setup_state);
|
|
+
|
|
+ if (req_state == TEVENT_REQ_DONE) {
|
|
+ state->cfg = NULL;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (state->cfg == NULL) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ state->cfg->num_doms = 0;
|
|
+ TALLOC_FREE(state->cfg->doms);
|
|
+ state->cfg = NULL;
|
|
+}
|
|
+
|
|
+static void wb_parent_idmap_setup_queue_wait_done(struct tevent_req *subreq);
|
|
+static bool wb_parent_idmap_setup_scan_config(const char *domname,
|
|
+ void *private_data);
|
|
+static void wb_parent_idmap_setup_lookupname_next(struct tevent_req *req);
|
|
+static void wb_parent_idmap_setup_lookupname_done(struct tevent_req *subreq);
|
|
+
|
|
+struct tevent_req *wb_parent_idmap_setup_send(TALLOC_CTX *mem_ctx,
|
|
+ struct tevent_context *ev)
|
|
+{
|
|
+ struct tevent_req *req = NULL;
|
|
+ struct wb_parent_idmap_setup_state *state = NULL;
|
|
+ struct tevent_req *subreq = NULL;
|
|
+
|
|
+ req = tevent_req_create(mem_ctx, &state,
|
|
+ struct wb_parent_idmap_setup_state);
|
|
+ if (req == NULL) {
|
|
+ return NULL;
|
|
+ }
|
|
+ *state = (struct wb_parent_idmap_setup_state) {
|
|
+ .ev = ev,
|
|
+ .cfg = &static_parent_idmap_config,
|
|
+ .dom_idx = 0,
|
|
+ };
|
|
+
|
|
+ if (state->cfg->queue == NULL) {
|
|
+ state->cfg->queue = tevent_queue_create(NULL,
|
|
+ "wb_parent_idmap_config_queue");
|
|
+ if (tevent_req_nomem(state->cfg->queue, req)) {
|
|
+ return tevent_req_post(req, ev);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ subreq = tevent_queue_wait_send(state, state->ev, state->cfg->queue);
|
|
+ if (tevent_req_nomem(subreq, req)) {
|
|
+ return tevent_req_post(req, ev);
|
|
+ }
|
|
+ tevent_req_set_callback(subreq,
|
|
+ wb_parent_idmap_setup_queue_wait_done,
|
|
+ req);
|
|
+
|
|
+ return req;
|
|
+}
|
|
+
|
|
+static void wb_parent_idmap_setup_queue_wait_done(struct tevent_req *subreq)
|
|
+{
|
|
+ struct tevent_req *req =
|
|
+ tevent_req_callback_data(subreq,
|
|
+ struct tevent_req);
|
|
+ struct wb_parent_idmap_setup_state *state =
|
|
+ tevent_req_data(req,
|
|
+ struct wb_parent_idmap_setup_state);
|
|
+ bool ok;
|
|
+
|
|
+ /*
|
|
+ * Note we don't call TALLOC_FREE(subreq) here in order to block the
|
|
+ * queue until tevent_req_received() in wb_parent_idmap_setup_recv()
|
|
+ * will destroy it implicitly.
|
|
+ */
|
|
+ ok = tevent_queue_wait_recv(subreq);
|
|
+ if (!ok) {
|
|
+ DBG_ERR("tevent_queue_wait_recv() failed\n");
|
|
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (state->cfg->num_doms != 0) {
|
|
+ /*
|
|
+ * If we're not the first one we're done.
|
|
+ */
|
|
+ tevent_req_done(req);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * From this point we start changing state->cfg,
|
|
+ * which is &static_parent_idmap_config,
|
|
+ * so we better setup a cleanup function
|
|
+ * to undo the changes on failure.
|
|
+ */
|
|
+ tevent_req_set_cleanup_fn(req, wb_parent_idmap_setup_cleanup);
|
|
+
|
|
+ /*
|
|
+ * Put the passdb idmap domain first. We always need to try
|
|
+ * there first.
|
|
+ */
|
|
+ state->cfg->doms = talloc_zero_array(NULL,
|
|
+ struct wb_parent_idmap_config_dom,
|
|
+ 1);
|
|
+ if (tevent_req_nomem(state->cfg->doms, req)) {
|
|
+ return;
|
|
+ }
|
|
+ state->cfg->doms[0].low_id = 0;
|
|
+ state->cfg->doms[0].high_id = UINT_MAX;
|
|
+ state->cfg->doms[0].name = talloc_strdup(state->cfg->doms,
|
|
+ get_global_sam_name());
|
|
+ if (tevent_req_nomem(state->cfg->doms[0].name, req)) {
|
|
+ return;
|
|
+ }
|
|
+ state->cfg->num_doms += 1;
|
|
+
|
|
+ lp_scan_idmap_domains(wb_parent_idmap_setup_scan_config, req);
|
|
+ if (!tevent_req_is_in_progress(req)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ wb_parent_idmap_setup_lookupname_next(req);
|
|
+}
|
|
+
|
|
+static bool wb_parent_idmap_setup_scan_config(const char *domname,
|
|
+ void *private_data)
|
|
+{
|
|
+ struct tevent_req *req =
|
|
+ talloc_get_type_abort(private_data,
|
|
+ struct tevent_req);
|
|
+ struct wb_parent_idmap_setup_state *state =
|
|
+ tevent_req_data(req,
|
|
+ struct wb_parent_idmap_setup_state);
|
|
+ struct wb_parent_idmap_config_dom *map = NULL;
|
|
+ size_t i;
|
|
+ const char *range;
|
|
+ unsigned low_id, high_id;
|
|
+ int ret;
|
|
+
|
|
+ range = idmap_config_const_string(domname, "range", NULL);
|
|
+ if (range == NULL) {
|
|
+ DBG_DEBUG("No range for domain %s found\n", domname);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ ret = sscanf(range, "%u - %u", &low_id, &high_id);
|
|
+ if (ret != 2) {
|
|
+ DBG_DEBUG("Invalid range spec \"%s\" for domain %s\n",
|
|
+ range, domname);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (low_id > high_id) {
|
|
+ DBG_DEBUG("Invalid range %u - %u for domain %s\n",
|
|
+ low_id, high_id, domname);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ for (i=0; i<state->cfg->num_doms; i++) {
|
|
+ if (strequal(domname, state->cfg->doms[i].name)) {
|
|
+ map = &state->cfg->doms[i];
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (map == NULL) {
|
|
+ struct wb_parent_idmap_config_dom *tmp;
|
|
+ char *name;
|
|
+
|
|
+ name = talloc_strdup(state, domname);
|
|
+ if (name == NULL) {
|
|
+ DBG_ERR("talloc failed\n");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ tmp = talloc_realloc(
|
|
+ NULL, state->cfg->doms, struct wb_parent_idmap_config_dom,
|
|
+ state->cfg->num_doms+1);
|
|
+ if (tmp == NULL) {
|
|
+ DBG_ERR("talloc failed\n");
|
|
+ return false;
|
|
+ }
|
|
+ state->cfg->doms = tmp;
|
|
+
|
|
+ map = &state->cfg->doms[state->cfg->num_doms];
|
|
+ state->cfg->num_doms += 1;
|
|
+ ZERO_STRUCTP(map);
|
|
+ map->name = talloc_move(state->cfg->doms, &name);
|
|
+ }
|
|
+
|
|
+ map->low_id = low_id;
|
|
+ map->high_id = high_id;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static void wb_parent_idmap_setup_lookupname_next(struct tevent_req *req)
|
|
+{
|
|
+ struct wb_parent_idmap_setup_state *state =
|
|
+ tevent_req_data(req,
|
|
+ struct wb_parent_idmap_setup_state);
|
|
+ struct wb_parent_idmap_config_dom *dom =
|
|
+ &state->cfg->doms[state->dom_idx];
|
|
+ struct tevent_req *subreq = NULL;
|
|
+
|
|
+ next_domain:
|
|
+ if (state->dom_idx == state->cfg->num_doms) {
|
|
+ tevent_req_done(req);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (strequal(dom->name, "*")) {
|
|
+ state->dom_idx++;
|
|
+ goto next_domain;
|
|
+ }
|
|
+
|
|
+ subreq = wb_lookupname_send(state,
|
|
+ state->ev,
|
|
+ dom->name,
|
|
+ dom->name,
|
|
+ "",
|
|
+ LOOKUP_NAME_NO_NSS);
|
|
+ if (tevent_req_nomem(subreq, req)) {
|
|
+ return;
|
|
+ }
|
|
+ tevent_req_set_callback(subreq,
|
|
+ wb_parent_idmap_setup_lookupname_done,
|
|
+ req);
|
|
+}
|
|
+
|
|
+static void wb_parent_idmap_setup_lookupname_done(struct tevent_req *subreq)
|
|
+{
|
|
+ struct tevent_req *req =
|
|
+ tevent_req_callback_data(subreq,
|
|
+ struct tevent_req);
|
|
+ struct wb_parent_idmap_setup_state *state =
|
|
+ tevent_req_data(req,
|
|
+ struct wb_parent_idmap_setup_state);
|
|
+ struct wb_parent_idmap_config_dom *dom =
|
|
+ &state->cfg->doms[state->dom_idx];
|
|
+ enum lsa_SidType type;
|
|
+ NTSTATUS status;
|
|
+
|
|
+ status = wb_lookupname_recv(subreq, &dom->sid, &type);
|
|
+ TALLOC_FREE(subreq);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ DBG_ERR("Lookup domain name '%s' failed '%s'\n",
|
|
+ dom->name,
|
|
+ nt_errstr(status));
|
|
+
|
|
+ state->dom_idx++;
|
|
+ wb_parent_idmap_setup_lookupname_next(req);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (type != SID_NAME_DOMAIN) {
|
|
+ struct dom_sid_buf buf;
|
|
+
|
|
+ DBG_ERR("SID %s for idmap domain name '%s' "
|
|
+ "not a domain SID\n",
|
|
+ dom_sid_str_buf(&dom->sid, &buf),
|
|
+ dom->name);
|
|
+
|
|
+ ZERO_STRUCT(dom->sid);
|
|
+ }
|
|
+
|
|
+ state->dom_idx++;
|
|
+ wb_parent_idmap_setup_lookupname_next(req);
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+NTSTATUS wb_parent_idmap_setup_recv(struct tevent_req *req,
|
|
+ const struct wb_parent_idmap_config **_cfg)
|
|
+{
|
|
+ const struct wb_parent_idmap_config *cfg = &static_parent_idmap_config;
|
|
+ NTSTATUS status;
|
|
+
|
|
+ *_cfg = NULL;
|
|
+
|
|
+ if (tevent_req_is_nterror(req, &status)) {
|
|
+ tevent_req_received(req);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Note state->cfg is already set to NULL by
|
|
+ * wb_parent_idmap_setup_cleanup()
|
|
+ */
|
|
+ *_cfg = cfg;
|
|
+ tevent_req_received(req);
|
|
+ return NT_STATUS_OK;
|
|
+}
|
|
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
|
|
index 97c38018aac..8923bb3124f 100644
|
|
--- a/source3/winbindd/winbindd_proto.h
|
|
+++ b/source3/winbindd/winbindd_proto.h
|
|
@@ -364,6 +364,11 @@ NTSTATUS winbindd_print_groupmembers(struct db_context *members,
|
|
|
|
/* The following definitions come from winbindd/winbindd_idmap.c */
|
|
|
|
+struct tevent_req *wb_parent_idmap_setup_send(TALLOC_CTX *mem_ctx,
|
|
+ struct tevent_context *ev);
|
|
+NTSTATUS wb_parent_idmap_setup_recv(struct tevent_req *req,
|
|
+ const struct wb_parent_idmap_config **_cfg);
|
|
+
|
|
void init_idmap_child(void);
|
|
struct winbindd_child *idmap_child(void);
|
|
struct dcerpc_binding_handle *idmap_child_handle(void);
|
|
--
|
|
2.23.0
|
|
|