Files
teleport/session/reexec/exec.go
T
Edoardo Spadolini 825b23049e session-helper: move srv.RunAndExit to session/reexec (#65263)
* Move the reexec parts of lib/srv to session/reexec

* Update references to the moved bits of lib/srv

* Avoid testutils in session/reexec

* Shuffle some constants around to avoid imports in session/reexec

* Vendor in the relevant parts of logutils in session/reexec

* Remove unnecessary symlink checks when opening files

* Inline the last two things from lib/utils in session/reexec

* Inline moved constants

* Deprecate group name consts and fix missed inlines

* Add missing session/reexec.TestMain with reexec check

* Reuse existing constants from the log constants package in session

* Fix broken godoc link
2026-04-08 09:40:52 +00:00

126 lines
4.1 KiB
Go

// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package reexec
import (
"bufio"
"context"
"errors"
"log/slog"
"os"
"os/exec"
"strings"
"syscall"
"github.com/gravitational/teleport/session/reexec/reexecconstants"
)
const (
defaultPath = "/bin:/usr/bin:/usr/local/bin:/sbin"
defaultEnvPath = "PATH=" + defaultPath
defaultRootPath = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
defaultEnvRootPath = "PATH=" + defaultRootPath
defaultLoginDefsPath = "/etc/login.defs"
)
// GetDefaultEnvPath returns the default value of PATH environment variable for
// new logins (prior to shell) based on login.defs. Returns a string which
// looks like "PATH=/usr/bin:/bin"
func GetDefaultEnvPath(uid string) string {
return getDefaultEnvPathWithLoginDefs(uid, defaultLoginDefsPath)
}
func getDefaultEnvPathWithLoginDefs(uid string, loginDefsPath string) string {
envPath := defaultEnvPath
envRootPath := defaultEnvRootPath
// open file, if it doesn't exist return a default path and move on
f, err := os.Open(loginDefsPath)
if err != nil {
if uid == "0" {
slog.DebugContext(context.Background(), "Unable to open login.defs, returning default su path", "login_defs_path", loginDefsPath, "error", err, "default_path", defaultEnvRootPath)
return defaultEnvRootPath
}
slog.DebugContext(context.Background(), "Unable to open login.defs, returning default path", "login_defs_path", loginDefsPath, "error", err, "default_path", defaultEnvPath)
return defaultEnvPath
}
defer f.Close()
// read path from login.defs file (/etc/login.defs) line by line:
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// skip comments and empty lines:
if line == "" || line[0] == '#' {
continue
}
// look for a line that starts with ENV_PATH or ENV_SUPATH
fields := strings.Fields(line)
if len(fields) > 1 {
if fields[0] == "ENV_PATH" {
envPath = fields[1]
}
if fields[0] == "ENV_SUPATH" {
envRootPath = fields[1]
}
}
}
// if any error occurs while reading the file, return the default value
err = scanner.Err()
if err != nil {
if uid == "0" {
slog.WarnContext(context.Background(), "Unable to read login.defs, returning default su path", "login_defs_path", loginDefsPath, "error", err, "default_path", defaultEnvRootPath)
return defaultEnvRootPath
}
slog.WarnContext(context.Background(), "Unable to read login.defs, returning default path", "login_defs_path", loginDefsPath, "error", err, "default_path", defaultEnvPath)
return defaultEnvPath
}
// if requesting path for uid 0 and no ENV_SUPATH is given, fallback to
// ENV_PATH first, then the default path.
if uid == "0" {
return envRootPath
}
return envPath
}
// exitCode extracts and returns the exit code from the error.
func exitCode(err error) int {
// If no error occurred, return 0 (success).
if err == nil {
return reexecconstants.RemoteCommandSuccess
}
var execExitErr *exec.ExitError
switch {
// Local execution.
case errors.As(err, &execExitErr):
waitStatus, ok := execExitErr.Sys().(syscall.WaitStatus)
if !ok {
return reexecconstants.RemoteCommandFailure
}
return waitStatus.ExitStatus()
// An error occurred, but the type is unknown, return a generic 255 code.
default:
slog.DebugContext(context.Background(), "Unknown error returned when executing command", "error", err)
return reexecconstants.RemoteCommandFailure
}
}