Skip to content

Simplify regex and fix tests for pamparse #1712

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions pkg/shellexec/shellexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,8 @@ const etcEnvironmentPath = "/etc/environment"
const etcSecurityPath = "/etc/security/pam_env.conf"
const userEnvironmentPath = "~/.pam_environment"

var pamParseOpts *pamparse.PamParseOpts = pamparse.ParsePasswdSafe()

/*
tryGetPamEnvVars tries to get the environment variables from /etc/environment,
/etc/security/pam_env.conf, and ~/.pam_environment.
Expand All @@ -556,11 +558,11 @@ func tryGetPamEnvVars() map[string]string {
if err != nil {
log.Printf("error parsing %s: %v", etcEnvironmentPath, err)
}
envVars2, err := pamparse.ParseEnvironmentConfFile(etcSecurityPath)
envVars2, err := pamparse.ParseEnvironmentConfFile(etcSecurityPath, pamParseOpts)
if err != nil {
log.Printf("error parsing %s: %v", etcSecurityPath, err)
}
envVars3, err := pamparse.ParseEnvironmentConfFile(wavebase.ExpandHomeDirSafe(userEnvironmentPath))
envVars3, err := pamparse.ParseEnvironmentConfFile(wavebase.ExpandHomeDirSafe(userEnvironmentPath), pamParseOpts)
if err != nil {
log.Printf("error parsing %s: %v", userEnvironmentPath, err)
}
Expand Down
45 changes: 35 additions & 10 deletions pkg/util/pamparse/pamparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import (
"strings"
)

type PamParseOpts struct {
Home string
Shell string
}

// Parses a file in the format of /etc/environment. Accepts a path to the file and returns a map of environment variables.
func ParseEnvironmentFile(path string) (map[string]string, error) {
rtn := make(map[string]string)
Expand All @@ -33,14 +38,19 @@ func ParseEnvironmentFile(path string) (map[string]string, error) {
}

// Parses a file in the format of /etc/security/pam_env.conf or ~/.pam_environment. Accepts a path to the file and returns a map of environment variables.
func ParseEnvironmentConfFile(path string) (map[string]string, error) {
func ParseEnvironmentConfFile(path string, opts *PamParseOpts) (map[string]string, error) {
rtn := make(map[string]string)
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
home, shell, err := parsePasswd()
if opts == nil {
opts, err = ParsePasswd()
if err != nil {
return nil, err
}
}
if err != nil {
return nil, err
}
Expand All @@ -56,16 +66,16 @@ func ParseEnvironmentConfFile(path string) (map[string]string, error) {
continue
}
}
rtn[key] = replaceHomeAndShell(val, home, shell)
rtn[key] = replaceHomeAndShell(val, opts.Home, opts.Shell)
}
return rtn, nil
}

// Gets the home directory and shell from /etc/passwd for the current user.
func parsePasswd() (string, string, error) {
func ParsePasswd() (*PamParseOpts, error) {
file, err := os.Open("/etc/passwd")
if err != nil {
return "", "", err
return nil, err
}
defer file.Close()
userPrefix := fmt.Sprintf("%s:", os.Getenv("USER"))
Expand All @@ -75,15 +85,30 @@ func parsePasswd() (string, string, error) {
if strings.HasPrefix(line, userPrefix) {
parts := strings.Split(line, ":")
if len(parts) < 7 {
return "", "", fmt.Errorf("invalid passwd entry: insufficient fields")
return nil, fmt.Errorf("invalid passwd entry: insufficient fields")
}
return parts[5], parts[6], nil
return &PamParseOpts{
Home: parts[5],
Shell: parts[6],
}, nil
}
}
if err := scanner.Err(); err != nil {
return "", "", fmt.Errorf("error reading passwd file: %w", err)
return nil, fmt.Errorf("error reading passwd file: %w", err)
}
return nil, nil
}

/*
Gets the home directory and shell from /etc/passwd for the current user and returns a map of environment variables from /etc/security/pam_env.conf or ~/.pam_environment.
Returns nil if an error occurs.
*/
func ParsePasswdSafe() *PamParseOpts {
opts, err := ParsePasswd()
if err != nil {
return nil
}
return "", "", nil
return opts
}

// Replaces @{HOME} and @{SHELL} placeholders in a string with the provided values. Follows guidance from https://wiki.archlinux.org/title/Environment_variables#Using_pam_env
Expand All @@ -105,7 +130,7 @@ func parseEnvironmentLine(line string) (string, string) {
}

// Regex to parse a line from /etc/security/pam_env.conf or ~/.pam_environment. Follows the guidance from https://wiki.archlinux.org/title/Environment_variables#Using_pam_env
var confFileLineRe = regexp.MustCompile(`^([A-Z0-9_]+[A-Za-z0-9]*)\s+(?:(?:DEFAULT=)([^\s]+(?: \w+)*))\s*(?:(?:OVERRIDE=)([^\s]+(?: \w+)*))?\s*$`)
var confFileLineRe = regexp.MustCompile(`^([A-Z0-9_]+[A-Za-z0-9]*)\s+(?:(?:DEFAULT=)(\S+(?: \S+)*))\s*(?:(?:OVERRIDE=)(\S+(?: \S+)*))?\s*$`)

func parseEnvironmentConfLine(line string) (string, string) {
m := confFileLineRe.FindStringSubmatch(line)
Expand Down
9 changes: 6 additions & 3 deletions pkg/util/pamparse/pamparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,17 @@ FOO11="foo#bar"
}

// parse the file
got, err := pamparse.ParseEnvironmentConfFile(tempFile)
got, err := pamparse.ParseEnvironmentConfFile(tempFile, &pamparse.PamParseOpts{
Home: "/home/user",
Shell: "/bin/bash"},
)
if err != nil {
t.Fatalf("failed to parse pam environment conf file: %v", err)
}

want := map[string]string{
"TEST": "./config\\ s:@{HOME}/.config\\ state",
"FOO": "@{HOME}/.config\\ s",
"TEST": "./config\\ s:/home/user/.config\\ state",
"FOO": "/home/user/.config\\ s",
"STRING": "string",
"STRINGOVERRIDE": "string2:string",
"FOO11": "foo",
Expand Down
Loading