From 311535065e0144275c7f887d6dc4fe1ab9f8b17e Mon Sep 17 00:00:00 2001 From: Andrew Burke <31974658+atburke@users.noreply.github.com> Date: Thu, 6 Jan 2022 17:26:31 -0800 Subject: [PATCH] Fall back to "/" when home directory doesn't exist for `tsh ssh` (#9413) (#9661) This change drops ssh users into the root directory when their home directory doesn't exist. This is the same behavior as OpenSSH. --- lib/srv/exec_test.go | 15 ++++++++++++--- lib/srv/reexec.go | 26 +++++++++++++++++--------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/lib/srv/exec_test.go b/lib/srv/exec_test.go index d489196cb07b3..3949919069936 100644 --- a/lib/srv/exec_test.go +++ b/lib/srv/exec_test.go @@ -177,7 +177,7 @@ func (s *ExecSuite) TestOSCommandPrep(c *check.C) { // Empty command (simple shell). execCmd, err := s.ctx.ExecCommand() c.Assert(err, check.IsNil) - cmd, err := buildCommand(execCmd, nil, nil, nil) + cmd, err := buildCommand(execCmd, s.usr, nil, nil, nil) c.Assert(err, check.IsNil) c.Assert(cmd, check.NotNil) c.Assert(cmd.Path, check.Equals, "/bin/sh") @@ -190,7 +190,7 @@ func (s *ExecSuite) TestOSCommandPrep(c *check.C) { s.ctx.ExecRequest.SetCommand("ls -lh /etc") execCmd, err = s.ctx.ExecCommand() c.Assert(err, check.IsNil) - cmd, err = buildCommand(execCmd, nil, nil, nil) + cmd, err = buildCommand(execCmd, s.usr, nil, nil, nil) c.Assert(err, check.IsNil) c.Assert(cmd, check.NotNil) c.Assert(cmd.Path, check.Equals, "/bin/sh") @@ -203,11 +203,20 @@ func (s *ExecSuite) TestOSCommandPrep(c *check.C) { s.ctx.ExecRequest.SetCommand("top") execCmd, err = s.ctx.ExecCommand() c.Assert(err, check.IsNil) - cmd, err = buildCommand(execCmd, nil, nil, nil) + cmd, err = buildCommand(execCmd, s.usr, nil, nil, nil) c.Assert(err, check.IsNil) c.Assert(cmd.Path, check.Equals, "/bin/sh") c.Assert(cmd.Args, check.DeepEquals, []string{"/bin/sh", "-c", "top"}) c.Assert(cmd.SysProcAttr.Pdeathsig, check.Equals, syscall.SIGKILL) + + // Missing home directory + s.usr.HomeDir = "/wrong/place" + root := string(os.PathSeparator) + expectedEnv[2] = fmt.Sprintf("HOME=%s", root) + cmd, err = buildCommand(execCmd, s.usr, nil, nil, nil) + c.Assert(err, check.IsNil) + c.Assert(cmd.Dir, check.Equals, root) + c.Assert(cmd.Env, check.DeepEquals, expectedEnv) } func (s *ExecSuite) TestLoginDefsParser(c *check.C) { diff --git a/lib/srv/reexec.go b/lib/srv/reexec.go index 2291e3dcc8192..b21fc86f61b17 100644 --- a/lib/srv/reexec.go +++ b/lib/srv/reexec.go @@ -215,8 +215,13 @@ func RunCommand() (io.Writer, int, error) { pamEnvironment = pamContext.Environment() } + localUser, err := user.Lookup(c.Login) + if err != nil { + return errorWriter, teleport.RemoteCommandFailure, trace.Wrap(err) + } + // Build the actual command that will launch the shell. - cmd, err := buildCommand(&c, tty, pty, pamEnvironment) + cmd, err := buildCommand(&c, localUser, tty, pty, pamEnvironment) if err != nil { return errorWriter, teleport.RemoteCommandFailure, trace.Wrap(err) } @@ -359,14 +364,10 @@ func RunAndExit(commandType string) { // buildCommand constructs a command that will execute the users shell. This // function is run by Teleport while it's re-executing. -func buildCommand(c *ExecCommand, tty *os.File, pty *os.File, pamEnvironment []string) (*exec.Cmd, error) { +func buildCommand(c *ExecCommand, localUser *user.User, tty *os.File, pty *os.File, pamEnvironment []string) (*exec.Cmd, error) { var cmd exec.Cmd - // Lookup the UID and GID for the user. - localUser, err := user.Lookup(c.Login) - if err != nil { - return nil, trace.Wrap(err) - } + // Get UID and GID. uid, err := strconv.Atoi(localUser.Uid) if err != nil { return nil, trace.Wrap(err) @@ -423,11 +424,18 @@ func buildCommand(c *ExecCommand, tty *os.File, pty *os.File, pamEnvironment []s cmd.Args = []string{shellPath, "-c", c.Command} } + // Ensure that the user has a home directory. + // TODO: Generalize this to support Windows. + homeDir := string(os.PathSeparator) + if utils.IsDir(localUser.HomeDir) { + homeDir = localUser.HomeDir + } + // Create default environment for user. cmd.Env = []string{ "LANG=en_US.UTF-8", getDefaultEnvPath(localUser.Uid, defaultLoginDefsPath), - "HOME=" + localUser.HomeDir, + "HOME=" + homeDir, "USER=" + c.Login, "SHELL=" + shellPath, } @@ -450,7 +458,7 @@ func buildCommand(c *ExecCommand, tty *os.File, pty *os.File, pamEnvironment []s cmd.Env = append(cmd.Env, pamEnvironment...) // Set the home directory for the user. - cmd.Dir = localUser.HomeDir + cmd.Dir = homeDir // If a terminal was requested, connect std{in,out,err} to the TTY and set // the controlling TTY. Otherwise, connect std{in,out,err} to