summaryrefslogtreecommitdiff
path: root/source3/smbd/uid.c
diff options
context:
space:
mode:
authorRalph Boehme <slow@samba.org>2018-07-05 13:09:53 +0200
committerStefan Metzmacher <metze@samba.org>2018-07-25 17:49:06 +0200
commit2dd95c1c38b9e1ce32d3d1081b6ec177910087a4 (patch)
tree8d19e5b3422e3e0d61859a7ffb1a7010f2a33c2a /source3/smbd/uid.c
parent1251a536df4b1df58d9ddacab03d3ebe6f4e5b60 (diff)
downloadsamba-2dd95c1c38b9e1ce32d3d1081b6ec177910087a4.tar.gz
s3: vfs: add user_vfs_evg to connection_struct
This will be used to in order to pass down the impersonation magic from the SMB layer through the SMB_VFS layer. This includes the following options: smbd:force sync user path safe threadpool smbd:force sync user chdir safe threadpool smbd:force sync root path safe threadpool smbd:force sync root chdir safe threadpool They can be used in order to test the non linux code path on linux, once we get code that makes full use of the new infrastructure. Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> Signed-off-by: Stefan Metzmacher <metze@samba.org> Signed-off-by: Ralph Boehme <slow@samba.org>
Diffstat (limited to 'source3/smbd/uid.c')
-rw-r--r--source3/smbd/uid.c754
1 files changed, 734 insertions, 20 deletions
diff --git a/source3/smbd/uid.c b/source3/smbd/uid.c
index fcc4d51a698..41bb66e2df1 100644
--- a/source3/smbd/uid.c
+++ b/source3/smbd/uid.c
@@ -18,6 +18,7 @@
*/
#include "includes.h"
+#include "system/filesys.h"
#include "system/passwd.h"
#include "smbd/smbd.h"
#include "smbd/globals.h"
@@ -26,6 +27,12 @@
#include "passdb/lookup_sid.h"
#include "auth.h"
#include "lib/util/time_basic.h"
+#include "lib/pthreadpool/pthreadpool_tevent.h"
+
+static struct smb_vfs_ev_glue *smbd_impersonate_user_ev_glue_create(
+ struct connection_struct *conn,
+ uint64_t vuid,
+ struct auth_session_info *session_info);
struct smbd_impersonate_debug_state {
int dbg_lvl;
@@ -306,7 +313,8 @@ static void free_conn_session_info_if_unused(connection_struct *conn)
}
}
/* Not used, safe to free. */
- TALLOC_FREE(conn->user_ev_ctx);
+ conn->user_ev_ctx = NULL;
+ TALLOC_FREE(conn->user_vfs_evg);
TALLOC_FREE(conn->session_info);
}
@@ -432,10 +440,13 @@ static bool check_user_ok(connection_struct *conn,
}
free_conn_session_info_if_unused(conn);
conn->session_info = ent->session_info;
- conn->user_ev_ctx = ent->user_ev_ctx;
+ conn->user_vfs_evg = ent->user_vfs_evg;
conn->read_only = ent->read_only;
conn->share_access = ent->share_access;
conn->vuid = ent->vuid;
+ conn->user_ev_ctx = smb_vfs_ev_glue_ev_ctx(
+ conn->user_vfs_evg);
+ SMB_ASSERT(conn->user_ev_ctx != NULL);
return(True);
}
}
@@ -481,22 +492,12 @@ static bool check_user_ok(connection_struct *conn,
ent->session_info->unix_token->uid = sec_initial_uid();
}
- if (vuid == UID_FIELD_INVALID) {
- ent->user_ev_ctx = smbd_impersonate_conn_sess_create(
- conn->sconn->raw_ev_ctx, conn, ent->session_info);
- if (ent->user_ev_ctx == NULL) {
- TALLOC_FREE(ent->session_info);
- ent->vuid = UID_FIELD_INVALID;
- return false;
- }
- } else {
- ent->user_ev_ctx = smbd_impersonate_conn_vuid_create(
- conn->sconn->raw_ev_ctx, conn, vuid);
- if (ent->user_ev_ctx == NULL) {
- TALLOC_FREE(ent->session_info);
- ent->vuid = UID_FIELD_INVALID;
- return false;
- }
+ ent->user_vfs_evg = smbd_impersonate_user_ev_glue_create(conn,
+ vuid, ent->session_info);
+ if (ent->user_vfs_evg == NULL) {
+ TALLOC_FREE(ent->session_info);
+ ent->vuid = UID_FIELD_INVALID;
+ return false;
}
/*
@@ -511,7 +512,10 @@ static bool check_user_ok(connection_struct *conn,
free_conn_session_info_if_unused(conn);
conn->session_info = ent->session_info;
conn->vuid = ent->vuid;
- conn->user_ev_ctx = ent->user_ev_ctx;
+ conn->user_vfs_evg = ent->user_vfs_evg;
+ conn->user_ev_ctx = smb_vfs_ev_glue_ev_ctx(conn->user_vfs_evg);
+ SMB_ASSERT(conn->user_ev_ctx != NULL);
+
if (vuid == UID_FIELD_INVALID) {
/*
* Not strictly needed, just make it really
@@ -520,7 +524,7 @@ static bool check_user_ok(connection_struct *conn,
ent->read_only = false;
ent->share_access = 0;
ent->session_info = NULL;
- ent->user_ev_ctx = NULL;
+ ent->user_vfs_evg = NULL;
}
conn->read_only = readonly_share;
@@ -1932,3 +1936,713 @@ struct tevent_context *smbd_impersonate_guest_create(struct tevent_context *main
return ev;
}
+
+struct smbd_impersonate_tp_current_state {
+ const void *conn_ptr;
+ uint64_t vuid; /* SMB2 compat */
+ struct security_unix_token partial_ut;
+ bool chdir_safe;
+ int saved_cwd_fd;
+};
+
+static int smbd_impersonate_tp_current_state_destructor(
+ struct smbd_impersonate_tp_current_state *state)
+{
+ if (state->saved_cwd_fd != -1) {
+ smb_panic(__location__);
+ }
+
+ return 0;
+}
+
+static bool smbd_impersonate_tp_current_before_job(struct pthreadpool_tevent *wrap,
+ void *private_data,
+ struct pthreadpool_tevent *main,
+ const char *location)
+{
+ struct smbd_impersonate_tp_current_state *state =
+ talloc_get_type_abort(private_data,
+ struct smbd_impersonate_tp_current_state);
+
+ if (state->conn_ptr != current_user.conn) {
+ smb_panic(__location__);
+ }
+
+ if (state->vuid != current_user.vuid) {
+ smb_panic(__location__);
+ }
+
+ if (state->partial_ut.uid != current_user.ut.uid) {
+ smb_panic(__location__);
+ }
+
+ if (state->partial_ut.gid != current_user.ut.gid) {
+ smb_panic(__location__);
+ }
+
+ if (state->partial_ut.ngroups != current_user.ut.ngroups) {
+ smb_panic(__location__);
+ }
+
+ /*
+ * We don't verify the group list, we should have hit
+ * an assert before. We only want to catch programmer
+ * errors here!
+ *
+ * We just have a sync pool and want to make sure
+ * we're already in the correct state.
+ *
+ * So we don't do any active impersonation.
+ */
+
+ /*
+ * we may need to remember the current working directory
+ * and later restore it in the after_job hook.
+ */
+ if (state->chdir_safe) {
+ int open_flags = O_RDONLY;
+ bool ok;
+
+#ifdef O_DIRECTORY
+ open_flags |= O_DIRECTORY;
+#endif
+#ifdef O_CLOEXEC
+ open_flags |= O_CLOEXEC;
+#endif
+
+ state->saved_cwd_fd = open(".", open_flags);
+ if (state->saved_cwd_fd == -1) {
+ DBG_ERR("unable to open '.' with open_flags[0x%x] - %s\n",
+ open_flags, strerror(errno));
+ smb_panic("smbd_impersonate_tp_current_before_job: "
+ "unable to open cwd '.'");
+ return false;
+ }
+ ok = smb_set_close_on_exec(state->saved_cwd_fd);
+ SMB_ASSERT(ok);
+ }
+
+ return true;
+}
+
+static bool smbd_impersonate_tp_current_after_job(struct pthreadpool_tevent *wrap,
+ void *private_data,
+ struct pthreadpool_tevent *main,
+ const char *location)
+{
+ struct smbd_impersonate_tp_current_state *state =
+ talloc_get_type_abort(private_data,
+ struct smbd_impersonate_tp_current_state);
+ int ret;
+
+ /*
+ * There's no impersonation to revert.
+ *
+ * But we may need to reset the current working directory.
+ */
+ if (state->saved_cwd_fd == -1) {
+ return true;
+ }
+
+ ret = fchdir(state->saved_cwd_fd);
+ if (ret != 0) {
+ DBG_ERR("unable to fchdir to the original directory - %s\n",
+ strerror(errno));
+ smb_panic("smbd_impersonate_tp_current_after_job: "
+ "unable restore cwd with fchdir.");
+ return false;
+ }
+
+ close(state->saved_cwd_fd);
+ state->saved_cwd_fd = -1;
+
+ return true;
+}
+
+static const struct pthreadpool_tevent_wrapper_ops smbd_impersonate_tp_current_ops = {
+ .name = "smbd_impersonate_tp_current",
+ .before_job = smbd_impersonate_tp_current_before_job,
+ .after_job = smbd_impersonate_tp_current_after_job,
+};
+
+struct pthreadpool_tevent *smbd_impersonate_tp_current_create(
+ TALLOC_CTX *mem_ctx,
+ struct pthreadpool_tevent *sync_tp,
+ struct connection_struct *conn,
+ uint64_t vuid, bool chdir_safe,
+ const struct security_unix_token *unix_token)
+{
+ struct pthreadpool_tevent *wrap_tp = NULL;
+ struct smbd_impersonate_tp_current_state *state = NULL;
+ size_t max_threads;
+
+ max_threads = pthreadpool_tevent_max_threads(sync_tp);
+ SMB_ASSERT(max_threads == 0);
+
+ /*
+ * We have a fake threadpool without real threads.
+ * So we just provide a a wrapper that asserts that
+ * we are already in the required impersonation state.
+ */
+
+ wrap_tp = pthreadpool_tevent_wrapper_create(sync_tp,
+ mem_ctx,
+ &smbd_impersonate_tp_current_ops,
+ &state,
+ struct smbd_impersonate_tp_current_state);
+ if (wrap_tp == NULL) {
+ return NULL;
+ }
+
+ state->conn_ptr = conn;
+ state->vuid = vuid;
+ state->partial_ut = *unix_token;
+ state->partial_ut.groups = NULL;
+ state->chdir_safe = chdir_safe;
+ state->saved_cwd_fd = -1;
+
+ if (chdir_safe) {
+ pthreadpool_tevent_force_per_thread_cwd(wrap_tp, state);
+ }
+
+ talloc_set_destructor(state, smbd_impersonate_tp_current_state_destructor);
+
+ return wrap_tp;
+}
+
+struct smbd_impersonate_tp_sess_state {
+ const struct security_unix_token *unix_token;
+};
+
+static bool smbd_impersonate_tp_sess_before_job(struct pthreadpool_tevent *wrap,
+ void *private_data,
+ struct pthreadpool_tevent *main,
+ const char *location)
+{
+ struct smbd_impersonate_tp_sess_state *state =
+ talloc_get_type_abort(private_data,
+ struct smbd_impersonate_tp_sess_state);
+ int ret;
+
+ /* Become the correct credential on this thread. */
+ ret = set_thread_credentials(state->unix_token->uid,
+ state->unix_token->gid,
+ (size_t)state->unix_token->ngroups,
+ state->unix_token->groups);
+ if (ret != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool smbd_impersonate_tp_sess_after_job(struct pthreadpool_tevent *wrap,
+ void *private_data,
+ struct pthreadpool_tevent *main,
+ const char *location)
+{
+ /*
+ * We skip the 'unbecome' here, if the following
+ * job cares, it already called set_thread_credentials() again.
+ *
+ * fd based jobs on the raw pool, don't really care...
+ */
+ return true;
+}
+
+static const struct pthreadpool_tevent_wrapper_ops smbd_impersonate_tp_sess_ops = {
+ .name = "smbd_impersonate_tp_sess",
+ .before_job = smbd_impersonate_tp_sess_before_job,
+ .after_job = smbd_impersonate_tp_sess_after_job,
+};
+
+static struct pthreadpool_tevent *smbd_impersonate_tp_sess_create(
+ TALLOC_CTX *mem_ctx,
+ struct pthreadpool_tevent *main_tp,
+ struct auth_session_info *session_info)
+{
+ struct pthreadpool_tevent *wrap_tp = NULL;
+ struct smbd_impersonate_tp_sess_state *state = NULL;
+ size_t max_threads;
+
+ max_threads = pthreadpool_tevent_max_threads(main_tp);
+ SMB_ASSERT(max_threads > 0);
+
+ wrap_tp = pthreadpool_tevent_wrapper_create(main_tp,
+ mem_ctx,
+ &smbd_impersonate_tp_sess_ops,
+ &state,
+ struct smbd_impersonate_tp_sess_state);
+ if (wrap_tp == NULL) {
+ return NULL;
+ }
+
+ state->unix_token = copy_unix_token(state, session_info->unix_token);
+ if (state->unix_token == NULL) {
+ int saved_errno = errno;
+ TALLOC_FREE(wrap_tp);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ return wrap_tp;
+}
+
+struct smbd_impersonate_tp_become_state {
+ void (*become_fn)(void);
+ void (*unbecome_fn)(void);
+ bool chdir_safe;
+ int saved_cwd_fd;
+};
+
+static int smbd_impersonate_tp_become_state_destructor(
+ struct smbd_impersonate_tp_become_state *state)
+{
+ if (state->saved_cwd_fd != -1) {
+ smb_panic(__location__);
+ }
+
+ return 0;
+}
+
+
+static bool smbd_impersonate_tp_become_before_job(struct pthreadpool_tevent *wrap,
+ void *private_data,
+ struct pthreadpool_tevent *main,
+ const char *location)
+{
+ struct smbd_impersonate_tp_become_state *state =
+ talloc_get_type_abort(private_data,
+ struct smbd_impersonate_tp_become_state);
+
+ /*
+ * we may need to remember the current working directory
+ * and later restore it in the after_job hook.
+ */
+ if (state->chdir_safe) {
+ int open_flags = O_RDONLY;
+ bool ok;
+
+#ifdef O_DIRECTORY
+ open_flags |= O_DIRECTORY;
+#endif
+#ifdef O_CLOEXEC
+ open_flags |= O_CLOEXEC;
+#endif
+
+ state->saved_cwd_fd = open(".", open_flags);
+ if (state->saved_cwd_fd == -1) {
+ DBG_ERR("unable to open '.' with open_flags[0x%x] - %s\n",
+ open_flags, strerror(errno));
+ smb_panic("smbd_impersonate_tp_current_before_job: "
+ "unable to open cwd '.'");
+ return false;
+ }
+ ok = smb_set_close_on_exec(state->saved_cwd_fd);
+ SMB_ASSERT(ok);
+ }
+
+ /*
+ * The function should abort on error...
+ */
+ state->become_fn();
+
+ return true;
+}
+
+static bool smbd_impersonate_tp_become_after_job(struct pthreadpool_tevent *wrap,
+ void *private_data,
+ struct pthreadpool_tevent *main,
+ const char *location)
+{
+ struct smbd_impersonate_tp_become_state *state =
+ talloc_get_type_abort(private_data,
+ struct smbd_impersonate_tp_become_state);
+ int ret;
+
+ /*
+ * The function should abort on error...
+ */
+ state->unbecome_fn();
+
+ /*
+ * There's no impersonation to revert.
+ *
+ * But we may need to reset the current working directory.
+ */
+ if (state->saved_cwd_fd == -1) {
+ return true;
+ }
+
+ ret = fchdir(state->saved_cwd_fd);
+ if (ret != 0) {
+ DBG_ERR("unable to fchdir to the original directory - %s\n",
+ strerror(errno));
+ smb_panic("smbd_impersonate_tp_current_after_job: "
+ "unable restore cwd with fchdir.");
+ return false;
+ }
+
+ close(state->saved_cwd_fd);
+ state->saved_cwd_fd = -1;
+
+ return true;
+}
+
+static const struct pthreadpool_tevent_wrapper_ops smbd_impersonate_tp_become_ops = {
+ .name = "smbd_impersonate_tp_become",
+ .before_job = smbd_impersonate_tp_become_before_job,
+ .after_job = smbd_impersonate_tp_become_after_job,
+};
+
+struct pthreadpool_tevent *smbd_impersonate_tp_become_create(
+ TALLOC_CTX *mem_ctx,
+ struct pthreadpool_tevent *sync_tp,
+ bool chdir_safe,
+ void (*become_fn)(void),
+ void (*unbecome_fn)(void))
+{
+ struct pthreadpool_tevent *wrap_tp = NULL;
+ struct smbd_impersonate_tp_become_state *state = NULL;
+ size_t max_threads;
+
+ max_threads = pthreadpool_tevent_max_threads(sync_tp);
+ SMB_ASSERT(max_threads == 0);
+
+ /*
+ * We have a fake threadpool without real threads.
+ * So we just provide a a wrapper that asserts that
+ * we are already in the required impersonation state.
+ */
+
+ wrap_tp = pthreadpool_tevent_wrapper_create(sync_tp,
+ mem_ctx,
+ &smbd_impersonate_tp_become_ops,
+ &state,
+ struct smbd_impersonate_tp_become_state);
+ if (wrap_tp == NULL) {
+ return NULL;
+ }
+
+ state->become_fn = become_fn;
+ state->unbecome_fn = unbecome_fn;
+ state->chdir_safe = chdir_safe;
+ state->saved_cwd_fd = -1;
+
+ if (chdir_safe) {
+ pthreadpool_tevent_force_per_thread_cwd(wrap_tp, state);
+ }
+
+ talloc_set_destructor(state, smbd_impersonate_tp_become_state_destructor);
+
+ return wrap_tp;
+}
+
+struct smbd_impersonate_tp_root_state {
+ const struct security_unix_token *fallback_token;
+};
+
+static bool smbd_impersonate_tp_root_before_job(struct pthreadpool_tevent *wrap,
+ void *private_data,
+ struct pthreadpool_tevent *main,
+ const char *location)
+{
+ int ret;
+
+ /*
+ * Become root in this thread.
+ */
+ ret = set_thread_credentials(0, 0, 0, NULL);
+ if (ret != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool smbd_impersonate_tp_root_after_job(struct pthreadpool_tevent *wrap,
+ void *private_data,
+ struct pthreadpool_tevent *main,
+ const char *location)
+{
+ struct smbd_impersonate_tp_root_state *state =
+ talloc_get_type_abort(private_data,
+ struct smbd_impersonate_tp_root_state);
+ int ret;
+
+ /*
+ * Move to a non root token again.
+ * We just use the one of the user_ev_ctx.
+ *
+ * The main goal is that we don't leave
+ * a thread arround with a root token.
+ */
+ ret = set_thread_credentials(state->fallback_token->uid,
+ state->fallback_token->gid,
+ (size_t)state->fallback_token->ngroups,
+ state->fallback_token->groups);
+ if (ret != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static const struct pthreadpool_tevent_wrapper_ops smbd_impersonate_tp_root_ops = {
+ .name = "smbd_impersonate_tp_root",
+ .before_job = smbd_impersonate_tp_root_before_job,
+ .after_job = smbd_impersonate_tp_root_after_job,
+};
+
+static struct pthreadpool_tevent *smbd_impersonate_tp_root_create(
+ TALLOC_CTX *mem_ctx,
+ struct pthreadpool_tevent *main_tp,
+ int snum,
+ const struct security_unix_token *fallback_token)
+{
+ struct pthreadpool_tevent *wrap_tp = NULL;
+ struct smbd_impersonate_tp_root_state *state = NULL;
+ size_t max_threads;
+
+ max_threads = pthreadpool_tevent_max_threads(main_tp);
+ SMB_ASSERT(max_threads > 0);
+
+ wrap_tp = pthreadpool_tevent_wrapper_create(main_tp,
+ mem_ctx,
+ &smbd_impersonate_tp_root_ops,
+ &state,
+ struct smbd_impersonate_tp_root_state);
+ if (wrap_tp == NULL) {
+ return NULL;
+ }
+
+ state->fallback_token = copy_unix_token(state, fallback_token);
+ if (state->fallback_token == NULL) {
+ int saved_errno = errno;
+ TALLOC_FREE(wrap_tp);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ return wrap_tp;
+}
+
+static struct smb_vfs_ev_glue *smbd_impersonate_user_ev_glue_create(
+ struct connection_struct *conn,
+ uint64_t vuid,
+ struct auth_session_info *session_info)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct smb_vfs_ev_glue *user_vfs_evg = NULL;
+ struct tevent_context *user_ev_ctx = NULL;
+ struct pthreadpool_tevent *user_tp_fd_safe = NULL;
+ struct pthreadpool_tevent *user_tp_path_safe = NULL;
+ bool user_tp_path_sync = true;
+ struct pthreadpool_tevent *user_tp_chdir_safe = NULL;
+ bool user_tp_chdir_sync = true;
+ struct pthreadpool_tevent *root_tp_fd_safe = NULL;
+ struct pthreadpool_tevent *root_tp_path_safe = NULL;
+ bool root_tp_path_sync = true;
+ struct pthreadpool_tevent *root_tp_chdir_safe = NULL;
+ bool root_tp_chdir_sync = true;
+ size_t max_threads;
+
+ if (vuid == UID_FIELD_INVALID) {
+ user_ev_ctx = smbd_impersonate_conn_sess_create(
+ conn->sconn->raw_ev_ctx, conn, session_info);
+ if (user_ev_ctx == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ } else {
+ user_ev_ctx = smbd_impersonate_conn_vuid_create(
+ conn->sconn->raw_ev_ctx, conn, vuid);
+ if (user_ev_ctx == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+ SMB_ASSERT(talloc_reparent(conn, frame, user_ev_ctx));
+
+#ifdef HAVE_LINUX_THREAD_CREDENTIALS
+ user_tp_path_sync = lp_parm_bool(SNUM(conn),
+ "smbd",
+ "force sync user path safe threadpool",
+ false);
+ user_tp_chdir_sync = lp_parm_bool(SNUM(conn),
+ "smbd",
+ "force sync user chdir safe threadpool",
+ false);
+ root_tp_path_sync = lp_parm_bool(SNUM(conn),
+ "smbd",
+ "force sync root path safe threadpool",
+ false);
+ root_tp_chdir_sync = lp_parm_bool(SNUM(conn),
+ "smbd",
+ "force sync root chdir safe threadpool",
+ false);
+#endif
+
+ max_threads = pthreadpool_tevent_max_threads(conn->sconn->raw_thread_pool);
+ if (max_threads == 0) {
+ /*
+ * We don't have real threads, so we need to force
+ * the sync versions...
+ */
+ user_tp_path_sync = true;
+ user_tp_chdir_sync = true;
+ root_tp_path_sync = true;
+ root_tp_chdir_sync = true;
+ }
+
+ /*
+ * fd_safe is easy :-)
+ */
+ user_tp_fd_safe = conn->sconn->raw_thread_pool;
+ root_tp_fd_safe = conn->sconn->raw_thread_pool;
+
+ if (user_tp_path_sync) {
+ /*
+ * We don't have support for per thread credentials,
+ * so we just provide a sync thread pool with a wrapper
+ * that asserts that we are already in the required
+ * impersonation state.
+ */
+ user_tp_path_safe = smbd_impersonate_tp_current_create(conn,
+ conn->sconn->sync_thread_pool,
+ conn,
+ vuid,
+ false, /* chdir_safe */
+ session_info->unix_token);
+ if (user_tp_path_safe == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ } else {
+ user_tp_path_safe = smbd_impersonate_tp_sess_create(conn,
+ conn->sconn->raw_thread_pool,
+ session_info);
+ if (user_tp_path_safe == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+ SMB_ASSERT(talloc_reparent(conn, frame, user_tp_path_safe));
+
+ if (pthreadpool_tevent_per_thread_cwd(user_tp_path_safe)) {
+ user_tp_chdir_safe = user_tp_path_safe;
+ } else {
+ user_tp_chdir_sync = true;
+ }
+
+ if (user_tp_chdir_sync) {
+ /*
+ * We don't have support for per thread credentials,
+ * so we just provide a sync thread pool with a wrapper
+ * that asserts that we are already in the required
+ * impersonation state.
+ *
+ * And it needs to cleanup after [f]chdir() within
+ * the job...
+ */
+ user_tp_chdir_safe = smbd_impersonate_tp_current_create(conn,
+ conn->sconn->sync_thread_pool,
+ conn,
+ vuid,
+ true, /* chdir_safe */
+ session_info->unix_token);
+ if (user_tp_chdir_safe == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ SMB_ASSERT(talloc_reparent(conn, frame, user_tp_chdir_safe));
+ } else {
+ SMB_ASSERT(user_tp_chdir_safe != NULL);
+ }
+
+ if (root_tp_path_sync) {
+ /*
+ * We don't have support for per thread credentials,
+ * so we just provide a sync thread pool with a wrapper
+ * that wrapps the job in become_root()/unbecome_root().
+ */
+ root_tp_path_safe = smbd_impersonate_tp_become_create(conn,
+ conn->sconn->sync_thread_pool,
+ false, /* chdir_safe */
+ become_root,
+ unbecome_root);
+ if (root_tp_path_safe == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ } else {
+ root_tp_path_safe = smbd_impersonate_tp_root_create(conn,
+ conn->sconn->raw_thread_pool,
+ SNUM(conn),
+ session_info->unix_token);
+ if (root_tp_path_safe == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+ SMB_ASSERT(talloc_reparent(conn, frame, root_tp_path_safe));
+
+ if (pthreadpool_tevent_per_thread_cwd(root_tp_path_safe)) {
+ root_tp_chdir_safe = root_tp_path_safe;
+ } else {
+ root_tp_chdir_sync = true;
+ }
+
+ if (root_tp_chdir_sync) {
+ /*
+ * We don't have support for per thread credentials,
+ * so we just provide a sync thread pool with a wrapper
+ * that wrapps the job in become_root()/unbecome_root().
+ *
+ * And it needs to cleanup after [f]chdir() within
+ * the job...
+ */
+ root_tp_chdir_safe = smbd_impersonate_tp_become_create(conn,
+ conn->sconn->sync_thread_pool,
+ true, /* chdir_safe */
+ become_root,
+ unbecome_root);
+ if (root_tp_chdir_safe == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ SMB_ASSERT(talloc_reparent(conn, frame, root_tp_chdir_safe));
+ } else {
+ SMB_ASSERT(root_tp_chdir_safe != NULL);
+ }
+
+ user_vfs_evg = smb_vfs_ev_glue_create(conn,
+ user_ev_ctx,
+ user_tp_fd_safe,
+ user_tp_path_safe,
+ user_tp_chdir_safe,
+ conn->sconn->root_ev_ctx,
+ root_tp_fd_safe,
+ root_tp_path_safe,
+ root_tp_chdir_safe);
+ if (user_vfs_evg == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ /*
+ * Make sure everything is a talloc child of user_vfs_evg
+ */
+ SMB_ASSERT(talloc_reparent(frame, user_vfs_evg, user_ev_ctx));
+ SMB_ASSERT(talloc_reparent(frame, user_vfs_evg, user_tp_path_safe));
+ if (user_tp_path_safe != user_tp_chdir_safe) {
+ SMB_ASSERT(talloc_reparent(frame, user_vfs_evg, user_tp_chdir_safe));
+ }
+ SMB_ASSERT(talloc_reparent(frame, user_vfs_evg, root_tp_path_safe));
+ if (root_tp_path_safe != root_tp_chdir_safe) {
+ SMB_ASSERT(talloc_reparent(frame, user_vfs_evg, root_tp_chdir_safe));
+ }
+
+ TALLOC_FREE(frame);
+ return user_vfs_evg;
+}