diff --git a/nativessh/pty.go b/nativessh/pty.go index cb5fa88..3d20e35 100644 --- a/nativessh/pty.go +++ b/nativessh/pty.go @@ -1,10 +1,13 @@ package nativessh import ( + "bufio" "errors" "fmt" "os" "os/exec" + "os/user" + "strings" "sync" "github.com/creack/pty" @@ -31,6 +34,42 @@ func findShell() string { return "/bin/sh" } +// userShell returns the login shell configured for u in /etc/passwd. +// If the field is empty or the binary does not exist, it falls back to +// findShell so there is always a usable shell. +func userShell(u *user.User) string { + if shell := passwdShell(u.Username); shell != "" { + if _, err := exec.LookPath(shell); err == nil { + return shell + } + } + return findShell() +} + +// passwdShell reads /etc/passwd and returns the login shell for the named user. +// Returns "" if the user is not found or the file cannot be read. +func passwdShell(username string) string { + f, err := os.Open("/etc/passwd") + if err != nil { + return "" + } + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + if line == "" || line[0] == '#' { + continue + } + // Fields: username:password:uid:gid:gecos:home:shell + fields := strings.SplitN(line, ":", 7) + if len(fields) == 7 && fields[0] == username { + return fields[6] + } + } + _ = scanner.Err() + return "" +} + // NewPTYSession spawns the best available shell in a PTY. func NewPTYSession() (*PTYSession, error) { shell := findShell() diff --git a/nativessh/pty_unix.go b/nativessh/pty_unix.go index 7e89c15..1203535 100644 --- a/nativessh/pty_unix.go +++ b/nativessh/pty_unix.go @@ -4,6 +4,7 @@ package nativessh import ( "fmt" + "os" "os/exec" "os/user" "strconv" @@ -42,7 +43,15 @@ func NewPTYSessionAs(username string) (*PTYSession, error) { } } - shell := findShell() + shell := userShell(u) + + // Prefer the user's home directory as the working directory, but fall back + // to / if it does not exist (e.g. useradd was run without -m). + homeDir := u.HomeDir + if _, err := os.Stat(homeDir); err != nil { + homeDir = "/" + } + cmd := exec.Command(shell, "--login") cmd.Env = []string{ "TERM=xterm-256color", @@ -52,7 +61,7 @@ func NewPTYSessionAs(username string) (*PTYSession, error) { "SHELL=" + shell, "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", } - cmd.Dir = u.HomeDir + cmd.Dir = homeDir cmd.SysProcAttr = &syscall.SysProcAttr{ Credential: &syscall.Credential{ Uid: uint32(uid),