Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 30 Mar 2020 03:26:52 +0000 (UTC)
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r359434 - in head/usr.sbin/cron: cron crontab lib
Message-ID:  <202003300326.02U3QqO9046706@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Mon Mar 30 03:26:52 2020
New Revision: 359434
URL: https://svnweb.freebsd.org/changeset/base/359434

Log:
  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

Modified:
  head/usr.sbin/cron/cron/do_command.c
  head/usr.sbin/cron/crontab/crontab.5
  head/usr.sbin/cron/lib/entry.c

Modified: head/usr.sbin/cron/cron/do_command.c
==============================================================================
--- head/usr.sbin/cron/cron/do_command.c	Mon Mar 30 00:06:56 2020	(r359433)
+++ head/usr.sbin/cron/cron/do_command.c	Mon Mar 30 03:26:52 2020	(r359434)
@@ -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,23 +339,63 @@ 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) {
 					warn("putenv");
 					_exit(ERROR_EXIT);
 				}
+			}
+
+			/* 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

Modified: head/usr.sbin/cron/crontab/crontab.5
==============================================================================
--- head/usr.sbin/cron/crontab/crontab.5	Mon Mar 30 00:06:56 2020	(r359433)
+++ head/usr.sbin/cron/crontab/crontab.5	Mon Mar 30 03:26:52 2020	(r359434)
@@ -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

Modified: head/usr.sbin/cron/lib/entry.c
==============================================================================
--- head/usr.sbin/cron/lib/entry.c	Mon Mar 30 00:06:56 2020	(r359433)
+++ head/usr.sbin/cron/lib/entry.c	Mon Mar 30 03:26:52 2020	(r359434)
@@ -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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202003300326.02U3QqO9046706>