mirror of
https://github.com/fosrl/newt.git
synced 2026-09-27 08:19:06 +02:00
79 lines
1.8 KiB
Go
79 lines
1.8 KiB
Go
//go:build !windows
|
|
|
|
package nativessh
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/user"
|
|
"strconv"
|
|
"syscall"
|
|
|
|
"github.com/creack/pty"
|
|
)
|
|
|
|
// NewPTYSessionAs spawns an interactive shell in a PTY running as the given
|
|
// system user. The calling process must have sufficient privileges (typically
|
|
// root / CAP_SETUID) to switch to a different UID/GID.
|
|
func NewPTYSessionAs(username string) (*PTYSession, error) {
|
|
u, err := user.Lookup(username)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("user lookup %q: %w", username, err)
|
|
}
|
|
uid, err := strconv.ParseUint(u.Uid, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse uid: %w", err)
|
|
}
|
|
gid, err := strconv.ParseUint(u.Gid, 10, 32)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse gid: %w", err)
|
|
}
|
|
|
|
// Collect supplementary group IDs.
|
|
groupIDs, err := u.GroupIds()
|
|
if err != nil {
|
|
groupIDs = []string{}
|
|
}
|
|
var groups []uint32
|
|
for _, g := range groupIDs {
|
|
gval, err := strconv.ParseUint(g, 10, 32)
|
|
if err == nil {
|
|
groups = append(groups, uint32(gval))
|
|
}
|
|
}
|
|
|
|
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",
|
|
"HOME=" + u.HomeDir,
|
|
"USER=" + username,
|
|
"LOGNAME=" + username,
|
|
"SHELL=" + shell,
|
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
}
|
|
cmd.Dir = homeDir
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Credential: &syscall.Credential{
|
|
Uid: uint32(uid),
|
|
Gid: uint32(gid),
|
|
Groups: groups,
|
|
},
|
|
}
|
|
|
|
ptmx, err := pty.Start(cmd)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("pty start: %w", err)
|
|
}
|
|
return &PTYSession{ptmx: ptmx, cmd: cmd}, nil
|
|
}
|