cron: respect PATH from login.conf

As a followup to the use of login.conf environment vars (other than PATH) in
cron, this patch adds PATH (and HOME) to the list of login.conf settings
respected.

The new logic is as follows:

1. SHELL is always _PATH_BSHELL unless explicitly overridden in the crontab
file itself; no other settings are respected. This is unchanged.

2. PATH is taken from the first of: crontab file, login.conf, _PATH_DEFPATH

3. HOME is taken from the first of: crontab file, login.conf, passwd entry,
unset

4. The current directory for invoking the command is taken from the crontab
file's value of HOME (existing behavior), or the passwd entry, but not
anywhere else (so it might not equal HOME if that was set in login.conf).

Submitted by:	Andrew Gierth <andrew_tao173.riddles.org.uk>
Reviewed by:	sigsys_gmail.com
Differential Revision:	https://reviews.freebsd.org/D23597
This commit is contained in:
Kyle Evans 2020-03-30 03:26:52 +00:00
parent 661a2cb802
commit 89c7bb5613
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=359434
3 changed files with 79 additions and 34 deletions

View file

@ -97,6 +97,7 @@ child_process(e, u)
register FILE *mail;
register int bytes = 1;
int status = 0;
const char *homedir = NULL;
# if defined(LOGIN_CAP)
struct passwd *pwd;
login_cap_t *lc;
@ -287,6 +288,14 @@ child_process(e, u)
pwd = getpwuid(e->uid);
lc = NULL;
if (pwd != NULL) {
if (pwd->pw_dir != NULL
&& pwd->pw_dir[0] != '\0') {
homedir = strdup(pwd->pw_dir);
if (homedir == NULL) {
warn("strdup");
_exit(ERROR_EXIT);
}
}
pwd->pw_gid = e->gid;
if (e->class != NULL)
lc = login_getclass(e->class);
@ -330,17 +339,38 @@ child_process(e, u)
if (lc != NULL)
login_close(lc);
#endif
chdir(env_get("HOME", e->envp));
/* exec the command.
/* For compatibility, we chdir to the value of HOME if it was
* specified explicitly in the crontab file, but not if it was
* set in the environment by some other mechanism. We chdir to
* the homedir given by the pw entry otherwise.
*
* If !LOGIN_CAP, then HOME is always set in e->envp.
*
* XXX: probably should also consult PAM.
*/
{
char *new_home = env_get("HOME", e->envp);
if (new_home != NULL && new_home[0] != '\0')
chdir(new_home);
else if (homedir != NULL)
chdir(homedir);
else
chdir("/");
}
/* exec the command. Note that SHELL is not respected from
* either login.conf or pw_shell, only an explicit setting
* in the crontab. (default of _PATH_BSHELL is supplied when
* setting up the entry)
*/
{
char *shell = env_get("SHELL", e->envp);
char **p;
/* Apply the environment from the entry, overriding existing
* values (this will always set PATH, LOGNAME, etc.) putenv
* should not fail unless malloc does.
/* Apply the environment from the entry, overriding
* existing values (this will always set LOGNAME and
* SHELL). putenv should not fail unless malloc does.
*/
for (p = e->envp; *p; ++p) {
if (putenv(*p) != 0) {
@ -349,6 +379,25 @@ child_process(e, u)
}
}
/* HOME in login.conf overrides pw, and HOME in the
* crontab overrides both. So set pw's value only if
* nothing was already set (overwrite==0).
*/
if (homedir != NULL
&& setenv("HOME", homedir, 0) < 0) {
warn("setenv(HOME)");
_exit(ERROR_EXIT);
}
/* PATH in login.conf is respected, but the crontab
* overrides; set a default value only if nothing
* already set.
*/
if (setenv("PATH", _PATH_DEFPATH, 0) < 0) {
warn("setenv(PATH)");
_exit(ERROR_EXIT);
}
# if DEBUGGING
if (DebugFlags & DTEST) {
fprintf(stderr,

View file

@ -17,7 +17,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd January 19, 2020
.Dd March 29, 2020
.Dt CRONTAB 5
.Os
.Sh NAME
@ -72,9 +72,6 @@ daemon.
.Ev SHELL
is set to
.Pa /bin/sh ,
.Ev PATH
is set to
.Pa /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin ,
and
.Ev LOGNAME
and
@ -83,12 +80,22 @@ are set from the
.Pa /etc/passwd
line of the crontab's owner.
In addition, the environment variables of the
user's login class, with the exception of
.Ev PATH ,
will be set from
user's login class will be set from
.Pa /etc/login.conf.db
and
.Pa ~/.login_conf .
(A setting of
.Ev HOME
in the login class will override the value from
.Pa /etc/passwd ,
but will not change the current directory when the command is
invoked, which can only be overridden with an explicit setting of
.Ev HOME
within the crontab file itself.)
If
.Ev PATH
is not set by any other means, it is defaulted to
.Pa /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin .
.Ev HOME ,
.Ev PATH
and
@ -109,17 +116,11 @@ On these systems,
.Ev USER
will be set also).
.Pp
In addition to
.Ev LOGNAME ,
.Ev HOME ,
.Ev PATH ,
and
.Ev SHELL ,
If
.Xr cron 8
will look at
.Ev MAILTO
if it has any reason to send mail as a result of running
commands in ``this'' crontab.
has any reason to send mail as a result of running commands in
``this'' crontab, it will respect the following settings which may be
defined in the crontab (but which are not taken from the login class).
If
.Ev MAILTO
is defined (and non-empty), mail is

View file

@ -369,7 +369,8 @@ load_entry(file, error_func, pw, envp)
e->gid = pw->pw_gid;
/* copy and fix up environment. some variables are just defaults and
* others are overrides.
* others are overrides; we process only the overrides here, defaults
* are handled in do_command after login.conf is processed.
*/
e->envp = env_copy(envp);
if (e->envp == NULL) {
@ -388,6 +389,10 @@ load_entry(file, error_func, pw, envp)
goto eof;
}
}
/* If LOGIN_CAP, this is deferred to do_command where the login class
* is processed. If !LOGIN_CAP, do it here.
*/
#ifndef LOGIN_CAP
if (!env_get("HOME", e->envp)) {
prev_env = e->envp;
sprintf(envstr, "HOME=%s", pw->pw_dir);
@ -399,17 +404,7 @@ load_entry(file, error_func, pw, envp)
goto eof;
}
}
if (!env_get("PATH", e->envp)) {
prev_env = e->envp;
sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
e->envp = env_set(e->envp, envstr);
if (e->envp == NULL) {
warn("env_set(%s)", envstr);
env_free(prev_env);
ecode = e_mem;
goto eof;
}
}
#endif
prev_env = e->envp;
sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
e->envp = env_set(e->envp, envstr);