smbd: call chdir_current_service() in change_to_user_internal() and pop_conn_ctx()
authorStefan Metzmacher <metze@samba.org>
Wed, 13 Jun 2018 11:30:33 +0000 (13:30 +0200)
committerStefan Metzmacher <metze@samba.org>
Mon, 18 Jun 2018 06:59:17 +0000 (08:59 +0200)
change_to_user() should be the one and only function for the whole
impersonation processing. So we also need to stack the
chdir_current_service() behaviour for become_user/unbecome_user,
so we may need to call vfs_ChDir(ctx_p->conn, ctx_p->conn->cwd_fname);
in pop_conn_ctx().

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/include/smb.h
source3/smbd/globals.h
source3/smbd/uid.c

index 5e83ee90afe29a7700cd96efc8612b6525b8186e..9ec65430852196c729b137b11b93f330e6d5040d 100644 (file)
@@ -156,6 +156,8 @@ struct sys_notify_context {
 struct current_user {
        struct connection_struct *conn;
        uint64_t vuid; /* SMB2 compat */
+       bool need_chdir;
+       bool done_chdir;
        struct security_unix_token ut;
        struct security_token *nt_user_token;
 };
index 384599be1df254b090a0365f5e9a3276800f5cbe..057645bedeebe2be918c1dde3c98990b6252a85f 100644 (file)
@@ -89,6 +89,8 @@ extern uint16_t fnf_handle;
 struct conn_ctx {
        connection_struct *conn;
        uint64_t vuid;
+       bool need_chdir;
+       bool done_chdir;
        userdom_struct user_info;
 };
 /* A stack of current_user connection contexts. */
index 913d4f3aa0a1bc13618bd434cee4e58be0bf2d2a..289727d957460fa03a65809cd5b94830f8776009 100644 (file)
@@ -52,6 +52,8 @@ bool change_to_guest(void)
 
        current_user.conn = NULL;
        current_user.vuid = UID_FIELD_INVALID;
+       current_user.need_chdir = false;
+       current_user.done_chdir = false;
 
        TALLOC_FREE(pass);
 
@@ -295,6 +297,7 @@ static bool change_to_user_internal(connection_struct *conn,
 
        if ((current_user.conn == conn) &&
            (current_user.vuid == vuid) &&
+           (current_user.need_chdir == conn->tcon_done) &&
            (current_user.ut.uid == session_info->unix_token->uid))
        {
                DBG_INFO("Skipping user change - already user\n");
@@ -369,12 +372,26 @@ static bool change_to_user_internal(connection_struct *conn,
 
        current_user.conn = conn;
        current_user.vuid = vuid;
+       current_user.need_chdir = conn->tcon_done;
 
-       DEBUG(5, ("Impersonated user: uid=(%d,%d), gid=(%d,%d)\n",
-                (int)getuid(),
-                (int)geteuid(),
-                (int)getgid(),
-                (int)getegid()));
+       if (current_user.need_chdir) {
+               ok = chdir_current_service(conn);
+               if (!ok) {
+                       DBG_ERR("chdir_current_service() failed!\n");
+                       return false;
+               }
+               current_user.done_chdir = true;
+       }
+
+       if (CHECK_DEBUGLVL(DBGLVL_INFO)) {
+               char cwdbuf[PATH_MAX+1] = { 0, };
+               DBG_INFO("Impersonated user: uid=(%d,%d), gid=(%d,%d), cwd=[%s]\n",
+                        (int)getuid(),
+                        (int)geteuid(),
+                        (int)getgid(),
+                        (int)getegid(),
+                        getcwd(cwdbuf, sizeof(cwdbuf)));
+       }
 
        return true;
 }
@@ -424,6 +441,8 @@ bool smbd_change_to_root_user(void)
 
        current_user.conn = NULL;
        current_user.vuid = UID_FIELD_INVALID;
+       current_user.need_chdir = false;
+       current_user.done_chdir = false;
 
        return(True);
 }
@@ -485,6 +504,8 @@ static void push_conn_ctx(void)
 
        ctx_p->conn = current_user.conn;
        ctx_p->vuid = current_user.vuid;
+       ctx_p->need_chdir = current_user.need_chdir;
+       ctx_p->done_chdir = current_user.done_chdir;
        ctx_p->user_info = current_user_info;
 
        DEBUG(4, ("push_conn_ctx(%llu) : conn_ctx_stack_ndx = %d\n",
@@ -510,11 +531,30 @@ static void pop_conn_ctx(void)
        set_current_user_info(ctx_p->user_info.smb_name,
                              ctx_p->user_info.unix_name,
                              ctx_p->user_info.domain);
+
+       /*
+        * Check if the current context did a chdir_current_service()
+        * and restore the cwd_fname of the previous context
+        * if needed.
+        */
+       if (current_user.done_chdir && ctx_p->need_chdir) {
+               int ret;
+
+               ret = vfs_ChDir(ctx_p->conn, ctx_p->conn->cwd_fname);
+               if (ret != 0) {
+                       DBG_ERR("vfs_ChDir() failed!\n");
+                       smb_panic("vfs_ChDir() failed!\n");
+               }
+       }
+
        current_user.conn = ctx_p->conn;
        current_user.vuid = ctx_p->vuid;
+       current_user.need_chdir = ctx_p->need_chdir;
+       current_user.done_chdir = ctx_p->done_chdir;
 
-       ctx_p->conn = NULL;
-       ctx_p->vuid = UID_FIELD_INVALID;
+       *ctx_p = (struct conn_ctx) {
+               .vuid = UID_FIELD_INVALID,
+       };
 }
 
 /****************************************************************************