diff --git a/pkg/shellexec/shellexec.go b/pkg/shellexec/shellexec.go index 6b1a60fe2..152933246 100644 --- a/pkg/shellexec/shellexec.go +++ b/pkg/shellexec/shellexec.go @@ -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. @@ -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) } diff --git a/pkg/util/pamparse/pamparse.go b/pkg/util/pamparse/pamparse.go index 950b6dbe3..f2ef14b74 100644 --- a/pkg/util/pamparse/pamparse.go +++ b/pkg/util/pamparse/pamparse.go @@ -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) @@ -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 } @@ -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")) @@ -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 + 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 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 @@ -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) diff --git a/pkg/util/pamparse/pamparse_test.go b/pkg/util/pamparse/pamparse_test.go index 223e5bdae..8e511030f 100644 --- a/pkg/util/pamparse/pamparse_test.go +++ b/pkg/util/pamparse/pamparse_test.go @@ -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",