diff options
Diffstat (limited to '')
-rw-r--r-- | libmisc/setupenv.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/libmisc/setupenv.c b/libmisc/setupenv.c new file mode 100644 index 0000000..5d7aefa --- /dev/null +++ b/libmisc/setupenv.c @@ -0,0 +1,290 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz + * SPDX-FileCopyrightText: 2001 - 2006, Tomasz Kłoczko + * SPDX-FileCopyrightText: 2007 - 2010, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Separated from setup.c. --marekm + */ + +#include <config.h> + +#ident "$Id$" + +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <ctype.h> +#include "prototypes.h" +#include "defines.h" +#include <pwd.h> +#include "getdef.h" +#include "shadowlog.h" + +#ifndef USE_PAM +static void +addenv_path (const char *varname, const char *dirname, const char *filename) +{ + char *buf; + size_t len = strlen (dirname) + strlen (filename) + 2; + int wlen; + + buf = xmalloc (len); + wlen = snprintf (buf, len, "%s/%s", dirname, filename); + assert (wlen == (int) len - 1); + + addenv (varname, buf); + free (buf); +} + +static void read_env_file (const char *filename) +{ + FILE *fp; + char buf[1024]; + char *cp, *name, *val; + + fp = fopen (filename, "r"); + if (NULL == fp) { + return; + } + while (fgets (buf, (int)(sizeof buf), fp) == buf) { + cp = strrchr (buf, '\n'); + if (NULL == cp) { + break; + } + *cp = '\0'; + + cp = buf; + /* ignore whitespace and comments */ + while (('\0' != *cp) && isspace (*cp)) { + cp++; + } + if (('\0' == *cp) || ('#' == *cp)) { + continue; + } + /* + * ignore lines which don't follow the name=value format + * (for example, the "export NAME" shell commands) + */ + name = cp; + while (('\0' != *cp) && !isspace (*cp) && ('=' != *cp)) { + cp++; + } + if ('=' != *cp) { + continue; + } + /* NUL-terminate the name */ + *cp = '\0'; + cp++; + val = cp; +#if 0 /* XXX untested, and needs rewrite with fewer goto's :-) */ +/* + (state, char_type) -> (state, action) + + state: unquoted, single_quoted, double_quoted, escaped, double_quoted_escaped + char_type: normal, white, backslash, single, double + action: remove_curr, remove_curr_skip_next, remove_prev, finish XXX +*/ + no_quote: + if (*cp == '\\') { + /* remove the backslash */ + remove_char (cp); + /* skip over the next character */ + if (*cp) + cp++; + goto no_quote; + } else if (*cp == '\'') { + /* remove the quote */ + remove_char (cp); + /* now within single quotes */ + goto s_quote; + } else if (*cp == '"') { + /* remove the quote */ + remove_char (cp); + /* now within double quotes */ + goto d_quote; + } else if (*cp == '\0') { + /* end of string */ + goto finished; + } else if (isspace (*cp)) { + /* unescaped whitespace - end of string */ + *cp = '\0'; + goto finished; + } else { + cp++; + goto no_quote; + } + s_quote: + if (*cp == '\'') { + /* remove the quote */ + remove_char (cp); + /* unquoted again */ + goto no_quote; + } else if (*cp == '\0') { + /* end of string */ + goto finished; + } else { + /* preserve everything within single quotes */ + cp++; + goto s_quote; + } + d_quote: + if (*cp == '\"') { + /* remove the quote */ + remove_char (cp); + /* unquoted again */ + goto no_quote; + } else if (*cp == '\\') { + cp++; + /* if backslash followed by double quote, remove backslash + else skip over the backslash and following char */ + if (*cp == '"') + remove_char (cp - 1); + else if (*cp) + cp++; + goto d_quote; + } + else if (*cp == '\0') { + /* end of string */ + goto finished; + } else { + /* preserve everything within double quotes */ + goto d_quote; + } + finished: +#endif /* 0 */ + /* + * XXX - should handle quotes, backslash escapes, etc. + * like the shell does. + */ + addenv (name, val); + } + (void) fclose (fp); +} +#endif /* USE_PAM */ + + +/* + * change to the user's home directory + * set the HOME, SHELL, MAIL, PATH, and LOGNAME or USER environmental + * variables. + */ + +void setup_env (struct passwd *info) +{ +#ifndef USE_PAM + const char *envf; +#endif + const char *cp; + + /* + * Change the current working directory to be the home directory + * of the user. It is a fatal error for this process to be unable + * to change to that directory. There is no "default" home + * directory. + * + * We no longer do it as root - should work better on NFS-mounted + * home directories. Some systems default to HOME=/, so we make + * this a configurable option. --marekm + */ + + if (chdir (info->pw_dir) == -1) { + static char temp_pw_dir[] = "/"; + + if (!getdef_bool ("DEFAULT_HOME") || chdir ("/") == -1) { + fprintf (log_get_logfd(), _("Unable to cd to '%s'\n"), + info->pw_dir); + SYSLOG ((LOG_WARN, + "unable to cd to `%s' for user `%s'\n", + info->pw_dir, info->pw_name)); + closelog (); + exit (EXIT_FAILURE); + } + (void) puts (_("No directory, logging in with HOME=/")); + free (info->pw_dir); + info->pw_dir = xstrdup (temp_pw_dir); + } + + /* + * Create the HOME environmental variable and export it. + */ + + addenv ("HOME", info->pw_dir); + + /* + * Create the SHELL environmental variable and export it. + */ + + if ((NULL == info->pw_shell) || ('\0' == *info->pw_shell)) { + static char temp_pw_shell[] = SHELL; + + free (info->pw_shell); + info->pw_shell = xstrdup (temp_pw_shell); + } + + addenv ("SHELL", info->pw_shell); + + /* + * Export the user name. For BSD derived systems, it's "USER", for + * all others it's "LOGNAME". We set both of them. + */ + + addenv ("USER", info->pw_name); + addenv ("LOGNAME", info->pw_name); + + /* + * Create the PATH environmental variable and export it. + */ + + cp = getdef_str ((info->pw_uid == 0) ? "ENV_SUPATH" : "ENV_PATH"); + + if (NULL == cp) { + /* not specified, use a minimal default */ + addenv ((info->pw_uid == 0) ? "PATH=/sbin:/bin:/usr/sbin:/usr/bin" : "PATH=/bin:/usr/bin", NULL); + } else if (strchr (cp, '=')) { + /* specified as name=value (PATH=...) */ + addenv (cp, NULL); + } else { + /* only value specified without "PATH=" */ + addenv ("PATH", cp); + } + +#ifndef USE_PAM + /* + * Create the MAIL environmental variable and export it. login.defs + * knows the prefix. + */ + + if (getdef_bool ("MAIL_CHECK_ENAB")) { + cp = getdef_str ("MAIL_DIR"); + if (NULL != cp) { + addenv_path ("MAIL", cp, info->pw_name); + } else { + cp = getdef_str ("MAIL_FILE"); + if (NULL != cp) { + addenv_path ("MAIL", info->pw_dir, cp); + } else { +#if defined(MAIL_SPOOL_FILE) + addenv_path ("MAIL", info->pw_dir, MAIL_SPOOL_FILE); +#elif defined(MAIL_SPOOL_DIR) + addenv_path ("MAIL", MAIL_SPOOL_DIR, info->pw_name); +#endif + } + } + } + + /* + * Read environment from optional config file. --marekm + */ + envf = getdef_str ("ENVIRON_FILE"); + if (NULL != envf) { + read_env_file (envf); + } +#endif /* !USE_PAM */ +} + |