Module Name:    src
Committed By:   christos
Date:           Fri Jun  9 17:36:30 UTC 2017

Modified Files:
        src/external/bsd/cron/bin/cron: Makefile
        src/external/bsd/cron/bin/crontab: Makefile
        src/external/bsd/cron/dist: Makefile config.h cron.c crontab.c
            database.c do_command.c funcs.h misc.c popen.c
Added Files:
        src/external/bsd/cron/dist: closeall.c pam_auth.c

Log Message:
Apply selected patches from OpenWall:
    http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/vixie-cron/

1. Add PAM support.
2. Sanitize children process reaping
3. futimens when we have an fd
4. close_all for crontab(8)
5. use a table for spool dirs instead of duplicating code.
6. handle errors from process_exit()
7. Add ENABLE_FIX_DIRECTORIES ifdef and enable it by default for compat
8. Avoid using fd's < STDERR

Not applied:
1. no xfork (no setresuid)
2. did not do the lstat before open.
3. did not enable cron group


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/external/bsd/cron/bin/cron/Makefile
cvs rdiff -u -r1.2 -r1.3 src/external/bsd/cron/bin/crontab/Makefile
cvs rdiff -u -r1.2 -r1.3 src/external/bsd/cron/dist/Makefile
cvs rdiff -u -r0 -r1.1 src/external/bsd/cron/dist/closeall.c \
    src/external/bsd/cron/dist/pam_auth.c
cvs rdiff -u -r1.4 -r1.5 src/external/bsd/cron/dist/config.h \
    src/external/bsd/cron/dist/popen.c
cvs rdiff -u -r1.9 -r1.10 src/external/bsd/cron/dist/cron.c
cvs rdiff -u -r1.13 -r1.14 src/external/bsd/cron/dist/crontab.c
cvs rdiff -u -r1.8 -r1.9 src/external/bsd/cron/dist/database.c
cvs rdiff -u -r1.7 -r1.8 src/external/bsd/cron/dist/do_command.c
cvs rdiff -u -r1.3 -r1.4 src/external/bsd/cron/dist/funcs.h \
    src/external/bsd/cron/dist/misc.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/bsd/cron/bin/cron/Makefile
diff -u src/external/bsd/cron/bin/cron/Makefile:1.3 src/external/bsd/cron/bin/cron/Makefile:1.4
--- src/external/bsd/cron/bin/cron/Makefile:1.3	Fri Jun 22 16:32:34 2012
+++ src/external/bsd/cron/bin/cron/Makefile	Fri Jun  9 13:36:29 2017
@@ -1,11 +1,12 @@
-#	$NetBSD: Makefile,v 1.3 2012/06/22 20:32:34 abs Exp $
+#	$NetBSD: Makefile,v 1.4 2017/06/09 17:36:29 christos Exp $
 
 BINDIR=	/usr/sbin
 PROG=	cron
 SRCS=	cron.c database.c do_command.c entry.c env.c job.c \
-	misc.c popen.c pw_dup.c user.c
-CPPFLAGS+=-I${.CURDIR} -DLOGIN_CAP
-LDADD+=-lutil
+	misc.c pam_auth.c popen.c pw_dup.c user.c
+CPPFLAGS+=-I${.CURDIR} -DLOGIN_CAP -DUSE_PAM
+DPADD+=${LIBPAM} ${LIBUTIL}
+LDADD+=-lpam -lutil
 MAN=	cron.8
 
 CWARNFLAGS.clang+=	-Wno-string-plus-int

Index: src/external/bsd/cron/bin/crontab/Makefile
diff -u src/external/bsd/cron/bin/crontab/Makefile:1.2 src/external/bsd/cron/bin/crontab/Makefile:1.3
--- src/external/bsd/cron/bin/crontab/Makefile:1.2	Fri May  7 17:54:07 2010
+++ src/external/bsd/cron/bin/crontab/Makefile	Fri Jun  9 13:36:29 2017
@@ -1,10 +1,10 @@
-#	$NetBSD: Makefile,v 1.2 2010/05/07 21:54:07 christos Exp $
+#	$NetBSD: Makefile,v 1.3 2017/06/09 17:36:29 christos Exp $
 
 .include <bsd.own.mk>
 
 USE_FORT?= yes	# setuid
 PROG=	crontab
-SRCS=	crontab.c misc.c entry.c env.c pw_dup.c
+SRCS=	crontab.c misc.c entry.c env.c pw_dup.c closeall.c
 CPPFLAGS+=-I${.CURDIR} -DDEBUGGING=1
 BINOWN =root
 BINMODE=4555

Index: src/external/bsd/cron/dist/Makefile
diff -u src/external/bsd/cron/dist/Makefile:1.2 src/external/bsd/cron/dist/Makefile:1.3
--- src/external/bsd/cron/dist/Makefile:1.2	Thu May  6 14:53:17 2010
+++ src/external/bsd/cron/dist/Makefile	Fri Jun  9 13:36:30 2017
@@ -86,14 +86,14 @@ MANPAGES	=	bitstring.3 crontab.5 crontab
 HEADERS		=	bitstring.h cron.h config.h pathnames.h externs.h \
 			macros.h structs.h funcs.h globals.h
 SOURCES		=	cron.c crontab.c database.c do_command.c entry.c \
-			env.c job.c user.c popen.c misc.c pw_dup.c
+			env.c job.c user.c popen.c misc.c pam_auth.c pw_dup.c
 SHAR_SOURCE	=	$(INFOS) $(MANPAGES) Makefile $(HEADERS) $(SOURCES)
 LINT_CRON	=	cron.c database.c user.c entry.c \
 			misc.c job.c do_command.c env.c popen.c pw_dup.c
 LINT_CRONTAB	=	crontab.c misc.c entry.c env.c
 CRON_OBJ	=	cron.o database.o user.o entry.o job.o do_command.o \
 			misc.o env.o popen.o pw_dup.o
-CRONTAB_OBJ	=	crontab.o misc.o entry.o env.o pw_dup.o
+CRONTAB_OBJ	=	crontab.o misc.o entry.o env.o pw_dup.o closeall.o
 
 all		:	cron crontab
 

Index: src/external/bsd/cron/dist/config.h
diff -u src/external/bsd/cron/dist/config.h:1.4 src/external/bsd/cron/dist/config.h:1.5
--- src/external/bsd/cron/dist/config.h:1.4	Sat Nov  3 11:39:23 2012
+++ src/external/bsd/cron/dist/config.h	Fri Jun  9 13:36:30 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: config.h,v 1.4 2012/11/03 15:39:23 christos Exp $	*/
+/*	$NetBSD: config.h,v 1.5 2017/06/09 17:36:30 christos Exp $	*/
 
 /* Copyright 1988,1990,1993,1994 by Paul Vixie
  * All rights reserved
@@ -94,6 +94,7 @@
 #define HAVE_FCHOWN		/*-*/
 #define HAVE_UTIMES		/*-*/
 #define HAVE_UTIMENSAT
+#define HAVE_FUTIMENS
 #define _INCOMPLETE_XOPEN_C063
 
 			/* if your OS supports a BSD-style login.conf file */
@@ -108,6 +109,11 @@
 			 * If this is not defined then crontab and at
 			 * must be setuid root.
 			 */
+			/* if your os supports PAM authentication */
+/*#define USE_PAM			*/
+
 /*#define CRON_GROUP	"crontab"	*/
 
+#define ENABLE_FIX_DIRECTORIES
+
 #define MAXTABSIZE_DEFAULT	(1024*256)
Index: src/external/bsd/cron/dist/popen.c
diff -u src/external/bsd/cron/dist/popen.c:1.4 src/external/bsd/cron/dist/popen.c:1.5
--- src/external/bsd/cron/dist/popen.c:1.4	Fri Sep  5 17:32:37 2014
+++ src/external/bsd/cron/dist/popen.c	Fri Jun  9 13:36:30 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: popen.c,v 1.4 2014/09/05 21:32:37 christos Exp $	*/
+/*	$NetBSD: popen.c,v 1.5 2017/06/09 17:36:30 christos Exp $	*/
 
 /*
  * Copyright (c) 1988, 1993, 1994
@@ -44,7 +44,7 @@
 static sccsid[] = "@(#)popen.c	8.3 (Berkeley) 4/6/94";
 static char rcsid[] = "Id: popen.c,v 1.6 2003/02/16 04:40:01 vixie Exp";
 #else
-__RCSID("$NetBSD: popen.c,v 1.4 2014/09/05 21:32:37 christos Exp $");
+__RCSID("$NetBSD: popen.c,v 1.5 2017/06/09 17:36:30 christos Exp $");
 #endif
 #endif /* not lint */
 
@@ -121,6 +121,11 @@ cron_popen(char *program, const char *ty
 				_exit(ERROR_EXIT);
 			}
 #endif /* BSD */
+#ifdef USE_PAM
+			if (!cron_pam_setcred())
+				_exit(1);
+			cron_pam_child_close();
+#endif
 			if (setuid(pw->pw_uid)) {
 				warn("unable to set uid for %s", pw->pw_name);
 				_exit(ERROR_EXIT);

Index: src/external/bsd/cron/dist/cron.c
diff -u src/external/bsd/cron/dist/cron.c:1.9 src/external/bsd/cron/dist/cron.c:1.10
--- src/external/bsd/cron/dist/cron.c:1.9	Sun Sep  7 09:34:12 2014
+++ src/external/bsd/cron/dist/cron.c	Fri Jun  9 13:36:30 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: cron.c,v 1.9 2014/09/07 13:34:12 joerg Exp $	*/
+/*	$NetBSD: cron.c,v 1.10 2017/06/09 17:36:30 christos Exp $	*/
 
 /* Copyright 1988,1990,1993,1994 by Paul Vixie
  * All rights reserved
@@ -25,7 +25,7 @@
 #if 0
 static char rcsid[] = "Id: cron.c,v 1.12 2004/01/23 18:56:42 vixie Exp";
 #else
-__RCSID("$NetBSD: cron.c,v 1.9 2014/09/07 13:34:12 joerg Exp $");
+__RCSID("$NetBSD: cron.c,v 1.10 2017/06/09 17:36:30 christos Exp $");
 #endif
 #endif
 
@@ -511,11 +511,10 @@ quit(int x __unused) {
 
 static void
 sigchld_reaper(void) {
-	WAIT_T waiter;
-	PID_T pid;
+	for (;;) {
+		WAIT_T waiter;
+		PID_T pid = waitpid(-1, &waiter, WNOHANG);
 
-	do {
-		pid = waitpid(-1, &waiter, WNOHANG);
 		switch (pid) {
 		case -1:
 			if (errno == EINTR)
@@ -523,19 +522,19 @@ sigchld_reaper(void) {
 			Debug(DPROC,
 			      ("[%ld] sigchld...no children\n",
 			       (long)getpid()));
-			break;
+			return;
 		case 0:
 			Debug(DPROC,
 			      ("[%ld] sigchld...no dead kids\n",
 			       (long)getpid()));
-			break;
+			return;
 		default:
 			Debug(DPROC,
 			      ("[%ld] sigchld...pid #%ld died, stat=%d\n",
 			       (long)getpid(), (long)pid, WEXITSTATUS(waiter)));
 			break;
 		}
-	} while (pid > 0);
+	}
 }
 
 static void

Index: src/external/bsd/cron/dist/crontab.c
diff -u src/external/bsd/cron/dist/crontab.c:1.13 src/external/bsd/cron/dist/crontab.c:1.14
--- src/external/bsd/cron/dist/crontab.c:1.13	Sun Jan  4 13:45:17 2015
+++ src/external/bsd/cron/dist/crontab.c	Fri Jun  9 13:36:30 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: crontab.c,v 1.13 2015/01/04 18:45:17 joerg Exp $	*/
+/*	$NetBSD: crontab.c,v 1.14 2017/06/09 17:36:30 christos Exp $	*/
 
 /* Copyright 1988,1990,1993,1994 by Paul Vixie
  * All rights reserved
@@ -25,7 +25,7 @@
 #if 0
 static char rcsid[] = "Id: crontab.c,v 1.12 2004/01/23 18:56:42 vixie Exp";
 #else
-__RCSID("$NetBSD: crontab.c,v 1.13 2015/01/04 18:45:17 joerg Exp $");
+__RCSID("$NetBSD: crontab.c,v 1.14 2017/06/09 17:36:30 christos Exp $");
 #endif
 #endif
 
@@ -429,7 +429,11 @@ edit_cmd(void) {
 	if (fflush(NewCrontab) < OK) {
 		err(ERROR_EXIT, "cannot flush output for `%s'", Filename);
 	}
+#ifdef HAVE_FUTIMENS
+	if (futimens(t, ts) == -1)
+#else
 	if (change_time(Filename, ts) == -1)
+#endif
 		err(ERROR_EXIT, "cannot set time info for `%s'", Filename);
  again:
 	rewind(NewCrontab);
@@ -465,6 +469,9 @@ edit_cmd(void) {
 		if (setuid(MY_UID(pw)) < 0) {
 			err(ERROR_EXIT, "cannot setuid(getuid())");
 		}
+		if (close_all(3)) {
+			err(ERROR_EXIT, "cannot close files");
+		}
 		if (chdir(_PATH_TMP) < 0) {
 			err(ERROR_EXIT, "cannot chdir to `%s'", _PATH_TMP);
 		}
@@ -682,7 +689,7 @@ replace_cmd(void) {
 	    "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
 	(void)fprintf(tmp,
 	    "# (Cron version %s -- %s)\n", CRON_VERSION,
-	    "$NetBSD: crontab.c,v 1.13 2015/01/04 18:45:17 joerg Exp $");
+	    "$NetBSD: crontab.c,v 1.14 2017/06/09 17:36:30 christos Exp $");
 
 	/* copy the crontab to the tmp
 	 */

Index: src/external/bsd/cron/dist/database.c
diff -u src/external/bsd/cron/dist/database.c:1.8 src/external/bsd/cron/dist/database.c:1.9
--- src/external/bsd/cron/dist/database.c:1.8	Mon Dec 24 14:30:46 2012
+++ src/external/bsd/cron/dist/database.c	Fri Jun  9 13:36:30 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: database.c,v 1.8 2012/12/24 19:30:46 christos Exp $	*/
+/*	$NetBSD: database.c,v 1.9 2017/06/09 17:36:30 christos Exp $	*/
 
 /* Copyright 1988,1990,1993,1994 by Paul Vixie
  * All rights reserved
@@ -25,7 +25,7 @@
 #if 0
 static char rcsid[] = "Id: database.c,v 1.7 2004/01/23 18:56:42 vixie Exp";
 #else
-__RCSID("$NetBSD: database.c,v 1.8 2012/12/24 19:30:46 christos Exp $");
+__RCSID("$NetBSD: database.c,v 1.9 2017/06/09 17:36:30 christos Exp $");
 #endif
 #endif
 
@@ -36,16 +36,33 @@ __RCSID("$NetBSD: database.c,v 1.8 2012/
 
 #define TMAX(a,b) ((a)>(b)?(a):(b))
 
+struct spooldir {
+	const char *path;
+	const char *uname;
+	const char *fname;
+	struct stat st;
+};
+
+static struct spooldir spools[] = {
+	{ .path = SPOOL_DIR, },
+	{ .path = CROND_DIR, .uname = "root", .fname = "*system*", },
+	{ .path = NULL, }
+};
+
 static	void		process_crontab(const char *, const char *,
 					const char *, struct stat *,
 					cron_db *, cron_db *);
 
 static void
-process_dir(const char *dname, struct stat *st, int sys, cron_db *new_db,
-    cron_db *old_db)
+process_dir(struct spooldir *sp, cron_db *new_db, cron_db *old_db)
 {
 	DIR *dir;
 	DIR_T *dp;
+	const char *dname = sp->path;
+	struct stat *st = &sp->st;
+
+	if (st->st_mtime == 0)
+		return;
 
 	/* we used to keep this dir open all the time, for the sake of
 	 * efficiency.  however, we need to close it in every fork, and
@@ -109,40 +126,41 @@ process_dir(const char *dname, struct st
 			continue;
 		}
 
-		process_crontab(sys ? "root" : fname, sys ? "*system*" :
-				fname, tabname, st, new_db, old_db);
+		process_crontab(sp->uname ? sp->uname : fname,
+				sp->fname ? sp->fname : fname,
+				tabname, st, new_db, old_db);
 	}
 	(void)closedir(dir);
 }
 
 void
 load_database(cron_db *old_db) {
-	struct stat spool_stat, syscron_stat, crond_stat;
+	struct stat syscron_stat;
 	cron_db new_db;
 	user *u, *nu;
-	time_t new_mtime;
+	time_t maxtime;
 
 	Debug(DLOAD, ("[%ld] load_database()\n", (long)getpid()));
 
-	/* before we start loading any data, do a stat on SPOOL_DIR
-	 * so that if anything changes as of this moment (i.e., before we've
-	 * cached any of the database), we'll see the changes next time.
-	 */
-	if (stat(SPOOL_DIR, &spool_stat) < OK) {
-		log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR);
-		(void) exit(ERROR_EXIT);
-	}
-
-	/* track system crontab directory
-	 */
-	if (stat(CROND_DIR, &crond_stat) < OK)
-		crond_stat.st_mtime = 0;
-
 	/* track system crontab file
 	 */
 	if (stat(SYSCRONTAB, &syscron_stat) < OK)
 		syscron_stat.st_mtime = 0;
 
+	maxtime = syscron_stat.st_mtime;
+	for (struct spooldir *p = spools; p->path; p++) {
+		if (stat(p->path, &p->st) < OK) {
+			if (errno == ENOENT) {
+				p->st.st_mtime = 0;
+				continue;
+			}
+			log_it("CRON", getpid(), "STAT FAILED", p->path);
+			(void) exit(ERROR_EXIT);
+		}
+		if (p->st.st_mtime > maxtime)
+			maxtime = p->st.st_mtime;
+	}
+
 	/* if spooldir's mtime has not changed, we don't need to fiddle with
 	 * the database.
 	 *
@@ -150,9 +168,7 @@ load_database(cron_db *old_db) {
 	 * so is guaranteed to be different than the stat() mtime the first
 	 * time this function is called.
 	 */
-	new_mtime = TMAX(crond_stat.st_mtime, TMAX(spool_stat.st_mtime,
-	    syscron_stat.st_mtime));
-	if (old_db->mtime == new_mtime) {
+	if (old_db->mtime == maxtime) {
 		Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n",
 			      (long)getpid()));
 		return;
@@ -163,17 +179,15 @@ load_database(cron_db *old_db) {
 	 * actually changed.  Whatever is left in the old database when
 	 * we're done is chaff -- crontabs that disappeared.
 	 */
-	new_db.mtime = new_mtime;
+	new_db.mtime = maxtime;
 	new_db.head = new_db.tail = NULL;
 
 	if (syscron_stat.st_mtime)
-		process_crontab("root", NULL, SYSCRONTAB, &syscron_stat,
-				&new_db, old_db);
-
-	if (crond_stat.st_mtime)
-		process_dir(CROND_DIR, &crond_stat, 1, &new_db, old_db);
+		process_crontab("root", "*system*", SYSCRONTAB,
+		    &syscron_stat, &new_db, old_db);
 
-	process_dir(SPOOL_DIR, &spool_stat, 0, &new_db, old_db);
+ 	for (struct spooldir *p = spools; p->path; p++)
+		process_dir(p, &new_db, old_db);
 
 	/* if we don't do this, then when our children eventually call
 	 * getpwnam() in do_command.c's child_process to verify MAILTO=,
@@ -240,14 +254,12 @@ process_crontab(const char *uname, const
 	mode_t eqmode = 0400, badmode = 0;
 	user *u;
 
-	if (fname == NULL) {
+	if (strcmp(fname, "*system*") == 0) {
 		/*
 		 * SYSCRONTAB:
-		 * set fname to something for logging purposes.
 		 * Allow it to become readable by group and others, but
 		 * not writable.
 		 */
-		fname = "*system*";
 		eqmode = 0;
 		badmode = 022;
 	} else if ((pw = getpwnam(uname)) == NULL) {

Index: src/external/bsd/cron/dist/do_command.c
diff -u src/external/bsd/cron/dist/do_command.c:1.7 src/external/bsd/cron/dist/do_command.c:1.8
--- src/external/bsd/cron/dist/do_command.c:1.7	Thu Dec 17 17:36:48 2015
+++ src/external/bsd/cron/dist/do_command.c	Fri Jun  9 13:36:30 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: do_command.c,v 1.7 2015/12/17 22:36:48 christos Exp $	*/
+/*	$NetBSD: do_command.c,v 1.8 2017/06/09 17:36:30 christos Exp $	*/
 
 /* Copyright 1988,1990,1993,1994 by Paul Vixie
  * All rights reserved
@@ -25,18 +25,20 @@
 #if 0
 static char rcsid[] = "Id: do_command.c,v 1.9 2004/01/23 18:56:42 vixie Exp";
 #else
-__RCSID("$NetBSD: do_command.c,v 1.7 2015/12/17 22:36:48 christos Exp $");
+__RCSID("$NetBSD: do_command.c,v 1.8 2017/06/09 17:36:30 christos Exp $");
 #endif
 #endif
 
 #include "cron.h"
 #include <unistd.h>
 
-static void		child_process(entry *);
+static int		child_process(entry *);
 static int		safe_p(const char *, const char *);
 
 void
 do_command(entry *e, user *u) {
+	int retval;
+
 	Debug(DPROC, ("[%ld] do_command(%s, (%s,%ld,%ld))\n",
 		      (long)getpid(), e->cmd, u->name,
 		      (long)e->pwd->pw_uid, (long)e->pwd->pw_gid));
@@ -55,10 +57,10 @@ do_command(entry *e, user *u) {
 	case 0:
 		/* child process */
 		acquire_daemonlock(1);
-		child_process(e);
-		Debug(DPROC, ("[%ld] child process done, exiting\n",
-			      (long)getpid()));
-		_exit(OK_EXIT);
+		retval = child_process(e);
+		Debug(DPROC, ("[%ld] child process done (rc=%d), exiting\n",
+			      (long)getpid(), retval));
+		_exit(retval);
 		break;
 	default:
 		/* parent process */
@@ -68,11 +70,32 @@ do_command(entry *e, user *u) {
 }
 
 static void
+sigchld_handler(int signo) {
+	for (;;) {
+		WAIT_T waiter;
+		PID_T pid = waitpid(-1, &waiter, WNOHANG);
+
+		switch (pid) {
+		case -1:
+			if (errno == EINTR)
+				continue;
+		case 0:
+			return;
+		default:
+			break;
+		}
+	}
+}
+
+extern char **environ;
+static int
 child_process(entry *e) {
 	int stdin_pipe[2], stdout_pipe[2];
 	char * volatile input_data;
 	char *homedir, *usernm, * volatile mailto;
-	int children = 0;
+	struct sigaction sact;
+	char **envp = e->envp;
+	int retval = OK_EXIT;
 
 	Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd));
 
@@ -81,14 +104,16 @@ child_process(entry *e) {
 	/* discover some useful and important environment settings
 	 */
 	usernm = e->pwd->pw_name;
-	mailto = env_get("MAILTO", e->envp);
+	mailto = env_get("MAILTO", envp);
 
-	/* our parent is watching for our death by catching SIGCHLD.  we
-	 * do not care to watch for our children's deaths this way -- we
-	 * use wait() explicitly.  so we have to reset the signal (which
-	 * was inherited from the parent).
-	 */
-	(void) signal(SIGCHLD, SIG_DFL);
+	memset(&sact, 0, sizeof(sact));
+	sigemptyset(&sact.sa_mask);
+	sact.sa_flags = 0;
+#ifdef SA_RESTART
+	sact.sa_flags |= SA_RESTART;
+#endif
+	sact.sa_handler = sigchld_handler;
+	(void) sigaction(SIGCHLD, &sact, NULL);
 
 	/* create some pipes to talk to our future child
 	 */
@@ -142,12 +167,22 @@ child_process(entry *e) {
 		*p = '\0';
 	}
 
+#ifdef USE_PAM
+	if (!cron_pam_start(usernm))
+		return ERROR_EXIT;
+
+	if (!(envp = cron_pam_getenvlist(envp))) {
+		retval = ERROR_EXIT;
+		goto child_process_end;
+	}
+#endif
+
 	/* fork again, this time so we can exec the user's command.
 	 */
 	switch (vfork()) {
 	case -1:
-		log_it("CRON", getpid(), "error", "can't vfork");
-		exit(ERROR_EXIT);
+		retval = ERROR_EXIT;
+		goto child_process_end;
 		/*NOTREACHED*/
 	case 0:
 		Debug(DPROC, ("[%ld] grandchild process vfork()'ed\n",
@@ -236,9 +271,9 @@ child_process(entry *e) {
 			 * we just added one via login.conf, add it to
 			 * the crontab environment.
 			 */
-			if (env_get("PATH", e->envp) == NULL) {
+			if (env_get("PATH", envp) == NULL && environ != NULL) {
 				if ((p = getenv("PATH")) != NULL)
-					e->envp = env_set(e->envp, p);
+					envp = env_set(envp, p);
 			}
 		}
 #else
@@ -259,6 +294,11 @@ child_process(entry *e) {
 			_exit(ERROR_EXIT);
 		}
 #endif /* BSD */
+#ifdef USE_PAM
+		if (!cron_pam_setcred())
+			_exit(ERROR_EXIT);
+		cron_pam_child_close();
+#endif
 		if (setuid(e->pwd->pw_uid) != 0) {
 			syslog(LOG_ERR, "setuid(%d) failed for %s: %m",
 			    e->pwd->pw_uid, e->pwd->pw_name);
@@ -266,7 +306,7 @@ child_process(entry *e) {
 		}
 		/* we aren't root after this... */
 #endif /* LOGIN_CAP */
-		homedir = env_get("HOME", e->envp);
+		homedir = env_get("HOME", envp);
 		if (chdir(homedir) != 0) {
 			syslog(LOG_ERR, "chdir(%s) $HOME failed for %s: %m",
 			    homedir, e->pwd->pw_name);
@@ -280,13 +320,15 @@ child_process(entry *e) {
 		 */
 		(void) signal(SIGCHLD, SIG_DFL);
 #endif
+		(void) signal(SIGPIPE, SIG_DFL);
+		(void) signal(SIGUSR1, SIG_DFL);
 		(void) signal(SIGHUP, SIG_DFL);
 
 		/*
 		 * Exec the command.
 		 */
 		{
-			char	*shell = env_get("SHELL", e->envp);
+			char	*shell = env_get("SHELL", envp);
 
 # if DEBUGGING
 			if (DebugFlags & DTEST) {
@@ -297,7 +339,7 @@ child_process(entry *e) {
 				_exit(OK_EXIT);
 			}
 # endif /*DEBUGGING*/
-			(void)execle(shell, shell, "-c", e->cmd, NULL, e->envp);
+			(void)execle(shell, shell, "-c", e->cmd, NULL, envp);
 			warn("execl: couldn't exec `%s'", shell);
 			_exit(ERROR_EXIT);
 		}
@@ -307,8 +349,6 @@ child_process(entry *e) {
 		break;
 	}
 
-	children++;
-
 	/* middle process, child of original cron, parent of process running
 	 * the user's command.
 	 */
@@ -341,6 +381,12 @@ child_process(entry *e) {
 		Debug(DPROC, ("[%ld] child2 sending data to grandchild\n",
 			      (long)getpid()));
 
+#ifdef USE_PAM
+		cron_pam_child_close();
+#else
+		log_close();
+#endif
+
 		/* close the pipe we don't use, since we inherited it and
 		 * are part of its reference count now.
 		 */
@@ -385,8 +431,6 @@ child_process(entry *e) {
 	 */
 	(void)close(stdin_pipe[WRITE_PIPE]);
 
-	children++;
-
 	/*
 	 * read output from the grandchild.  it's stderr has been redirected to
 	 * it's stdout, which has been redirected to our pipe.  if there is any
@@ -441,13 +485,15 @@ child_process(entry *e) {
 				if (strlens(MAILFMT, MAILARG, NULL) + 1
 				    >= sizeof mailcmd) {
 					warnx("mailcmd too long");
-					(void) _exit(ERROR_EXIT);
+					retval = ERROR_EXIT;
+					goto child_process_end;
 				}
 				(void)snprintf(mailcmd, sizeof(mailcmd), 
 				    MAILFMT, MAILARG);
 				if (!(mail = cron_popen(mailcmd, "w", e->pwd))) {
 					warn("cannot run `%s'", mailcmd);
-					(void) _exit(ERROR_EXIT);
+					retval = ERROR_EXIT;
+					goto child_process_end;
 				}
 				(void)fprintf(mail,
 				    "From: root (Cron Daemon)\n");
@@ -461,7 +507,7 @@ child_process(entry *e) {
 				(void)fprintf(mail, "Date: %s\n",
 					arpadate(&StartTime));
 #endif /*MAIL_DATE*/
-				for (env = e->envp;  *env;  env++)
+				for (env = envp;  *env;  env++)
 					(void)fprintf(mail,
 					    "X-Cron-Env: <%s>\n", *env);
 				(void)fprintf(mail, "\n");
@@ -522,26 +568,7 @@ child_process(entry *e) {
 
 	/* wait for children to die.
 	 */
-	for (; children > 0; children--) {
-		WAIT_T waiter;
-		PID_T pid;
-
-		Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n",
-			      (long)getpid(), children));
-		while ((pid = wait(&waiter)) < OK && errno == EINTR)
-			;
-		if (pid < OK) {
-			Debug(DPROC,
-			      ("[%ld] no more grandchildren--mail written?\n",
-			       (long)getpid()));
-			break;
-		}
-		Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x",
-			      (long)getpid(), (long)pid, WEXITSTATUS(waiter)));
-		if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
-			Debug(DPROC, (", dumped core"));
-		Debug(DPROC, ("\n"));
-	}
+	sigchld_handler(0);
 
 	/* Log the time when we finished deadling with the job */
 	/*local*/{
@@ -550,6 +577,12 @@ child_process(entry *e) {
 		log_it(usernm, getpid(), "CMD FINISH", x);
 		free(x);
 	}
+
+child_process_end:
+#ifdef USE_PAM
+	cron_pam_finish();
+#endif
+	return retval;
 }
 
 static int

Index: src/external/bsd/cron/dist/funcs.h
diff -u src/external/bsd/cron/dist/funcs.h:1.3 src/external/bsd/cron/dist/funcs.h:1.4
--- src/external/bsd/cron/dist/funcs.h:1.3	Thu Dec 17 17:36:48 2015
+++ src/external/bsd/cron/dist/funcs.h	Fri Jun  9 13:36:30 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: funcs.h,v 1.3 2015/12/17 22:36:48 christos Exp $	*/
+/*	$NetBSD: funcs.h,v 1.4 2017/06/09 17:36:30 christos Exp $	*/
 
 /*
  * Id: funcs.h,v 1.9 2004/01/23 18:56:42 vixie Exp
@@ -75,3 +75,12 @@ struct passwd	*pw_dup(const struct passw
 #ifndef HAVE_TM_GMTOFF
 long		get_gmtoff(time_t *, struct tm *);
 #endif
+
+extern int close_all(int);
+#ifdef USE_PAM
+extern int cron_pam_start (const char *user);
+extern int cron_pam_setcred (void);
+extern void cron_pam_finish (void);
+extern void cron_pam_child_close (void);
+extern char **cron_pam_getenvlist (char **envp);
+#endif
Index: src/external/bsd/cron/dist/misc.c
diff -u src/external/bsd/cron/dist/misc.c:1.3 src/external/bsd/cron/dist/misc.c:1.4
--- src/external/bsd/cron/dist/misc.c:1.3	Thu Dec 17 17:36:48 2015
+++ src/external/bsd/cron/dist/misc.c	Fri Jun  9 13:36:30 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: misc.c,v 1.3 2015/12/17 22:36:48 christos Exp $	*/
+/*	$NetBSD: misc.c,v 1.4 2017/06/09 17:36:30 christos Exp $	*/
 
 /* Copyright 1988,1990,1993,1994 by Paul Vixie
  * All rights reserved
@@ -25,7 +25,7 @@
 #if 0
 static char rcsid[] = "Id: misc.c,v 1.16 2004/01/23 18:56:43 vixie Exp";
 #else
-__RCSID("$NetBSD: misc.c,v 1.3 2015/12/17 22:36:48 christos Exp $");
+__RCSID("$NetBSD: misc.c,v 1.4 2017/06/09 17:36:30 christos Exp $");
 #endif
 #endif
 
@@ -220,6 +220,7 @@ set_cron_cwd(void) {
 #endif
 	/* first check for CRONDIR ("/var/cron" or some such)
 	 */
+#ifdef ENABLE_FIX_DIRECTORIES
 	if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
 		warn("Cannot stat `%s'", CRONDIR);
 		if (OK == mkdir(CRONDIR, 0710)) {
@@ -234,12 +235,14 @@ set_cron_cwd(void) {
 		errx(ERROR_EXIT, "`%s' is not a directory, bailing out.",
 			CRONDIR);
 	}
+#endif /* ENABLE_FIX_DIRECTORIES */
 	if (chdir(CRONDIR) < OK) {
 		err(ERROR_EXIT, "cannot chdir `%s', bailing out.\n", CRONDIR);
 	}
 
 	/* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
 	 */
+#ifdef ENABLE_FIX_DIRECTORIES
 	if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
 		warn("cannot stat `%s'", SPOOL_DIR);
 		if (OK == mkdir(SPOOL_DIR, 0700)) {
@@ -250,17 +253,33 @@ set_cron_cwd(void) {
 			err(ERROR_EXIT, "cannot create `%s'", SPOOL_DIR);
 		}
 	}
+#else
+	if (stat(SPOOL_DIR, &sb)) {
+		err(ERROR_EXIT, "cannot stat `%s'", SPOOL_DIR);
+	}
+#endif /* ENABLE_FIX_DIRECTORIES */
 	if (!S_ISDIR(sb.st_mode)) {
 		errx(ERROR_EXIT, "`%s' is not a directory, bailing out.",
 			SPOOL_DIR);
 	}
 	if (grp != NULL) {
-		if (sb.st_gid != grp->gr_gid)
+		if (sb.st_gid != grp->gr_gid) {
+#ifdef ENABLE_FIX_DIRECTORIES
+			errx(ERROR_EXIT, "Bad group %d != %d for `%s'",
+			    (int)sb.st_gid, (int)grp->gr_gid, SPOOL_DIR);
+#else				
 			if (chown(SPOOL_DIR, (uid_t)-1, grp->gr_gid) == -1)
 			    err(ERROR_EXIT, "cannot chown `%s'", SPOOL_DIR);
+#endif
+		}
 		if (sb.st_mode != 01730)
+#ifdef ENABLE_FIX_DIRECTORIES
+			errx(ERROR_EXIT, "Bad mode %#o != %#o for `%s'",
+			    (int)sb.st_mode, 01730, SPOOL_DIR);
+#else				
 			if (chmod(SPOOL_DIR, 01730) == -1)
 			    err(ERROR_EXIT, "cannot chmod `%s'", SPOOL_DIR);
+#endif
 	}
 }
 
@@ -299,6 +318,17 @@ acquire_daemonlock(int closeflag) {
 			log_it("CRON", getpid(), "DEATH", buf);
 			errx(ERROR_EXIT, "%s", buf);
 		}
+		/* fd must be > STDERR since we dup fd 0-2 to /dev/null */
+		if (fd <= STDERR) {
+			if (dup2(fd, STDERR + 1) < 0) {
+				snprintf(buf, sizeof buf,
+				    "can't dup pid fd: %s", strerror(errno));
+				log_it("CRON", getpid(), "DEATH", buf);
+ 				exit(ERROR_EXIT);
+ 			}
+			close(fd);
+			fd = STDERR + 1;
+		}
 
 		if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
 			int save_errno = errno;

Added files:

Index: src/external/bsd/cron/dist/closeall.c
diff -u /dev/null src/external/bsd/cron/dist/closeall.c:1.1
--- /dev/null	Fri Jun  9 13:36:30 2017
+++ src/external/bsd/cron/dist/closeall.c	Fri Jun  9 13:36:30 2017
@@ -0,0 +1,34 @@
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef __linux__
+#include <linux/limits.h>
+#endif
+
+#include "cron.h"
+
+int close_all(int start)
+{
+#ifdef F_CLOSEM
+	return fcntl(start, F_CLOSEM);
+#else
+	int fd, max;
+
+	max = sysconf(_SC_OPEN_MAX);
+	if (max <= 0)
+		return -1;
+
+#ifdef __linux__
+	if (max < NR_OPEN)
+		max = NR_OPEN;
+#endif
+
+	for (fd = start; fd < max; fd++) {
+		if (close(fd) && errno != EBADF)
+			return -1;
+	}
+
+	return 0;
+#endif
+}
Index: src/external/bsd/cron/dist/pam_auth.c
diff -u /dev/null src/external/bsd/cron/dist/pam_auth.c:1.1
--- /dev/null	Fri Jun  9 13:36:30 2017
+++ src/external/bsd/cron/dist/pam_auth.c	Fri Jun  9 13:36:30 2017
@@ -0,0 +1,121 @@
+#include "cron.h"
+
+#ifdef USE_PAM
+
+#include <security/pam_appl.h>
+
+static pam_handle_t *pamh = NULL;
+static const struct pam_conv cron_conv = { 0 };
+
+int
+cron_pam_start (const char *username)
+{
+	int     retval;
+
+	if (pamh)
+		return 0;
+
+	retval = pam_start ("cron", username, &cron_conv, &pamh);
+	log_close ();
+	if (retval != PAM_SUCCESS)
+	{
+		pamh = NULL;
+		log_it ("CRON", getpid (), "pam_start failed",
+			pam_strerror (pamh, retval));
+		return 0;
+	}
+	retval = pam_authenticate (pamh, PAM_SILENT);
+	log_close ();
+	if (retval != PAM_SUCCESS)
+	{
+		log_it ("CRON", getpid (), "pam_authenticate failed",
+			pam_strerror (pamh, retval));
+		pam_end (pamh, retval);
+		pamh = NULL;
+		return 0;
+	}
+	retval = pam_acct_mgmt (pamh, PAM_SILENT);
+	log_close ();
+	if (retval != PAM_SUCCESS)
+	{
+		log_it ("CRON", getpid (), "pam_acct_mgmt failed",
+			pam_strerror (pamh, retval));
+		pam_end (pamh, retval);
+		pamh = NULL;
+		return 0;
+	}
+	retval = pam_open_session (pamh, PAM_SILENT);
+	log_close ();
+	if (retval != PAM_SUCCESS)
+	{
+		log_it ("CRON", getpid (), "pam_open_session failed",
+			pam_strerror (pamh, retval));
+		pam_end (pamh, retval);
+		pamh = NULL;
+		return 0;
+	}
+
+	return 1;
+}
+
+int
+cron_pam_setcred (void)
+{
+	int     retval;
+
+	if (!pamh)
+		return 0;
+
+	retval = pam_setcred (pamh, PAM_ESTABLISH_CRED | PAM_SILENT);
+	log_close ();
+	if (retval != PAM_SUCCESS)
+	{
+		log_it ("CRON", getpid (), "pam_setcred failed",
+			pam_strerror (pamh, retval));
+		pam_end (pamh, retval);
+		pamh = NULL;
+		log_close ();
+		return 0;
+	}
+
+	return 1;
+}
+
+void
+cron_pam_finish (void)
+{
+	if (!pamh)
+		return;
+
+	pam_close_session (pamh, 0);
+	pam_end (pamh, 0);
+	pamh = NULL;
+	log_close ();
+}
+
+#ifndef PAM_DATA_SILENT
+#define PAM_DATA_SILENT 0
+#endif
+
+void
+cron_pam_child_close (void)
+{
+	pam_end (pamh, PAM_DATA_SILENT);
+	pamh = NULL;
+	log_close ();
+}
+
+char  **
+cron_pam_getenvlist (char **envp)
+{
+	if (!pamh || !envp)
+		return 0;
+
+	for (; *envp; ++envp)
+		if (pam_putenv (pamh, *envp) != PAM_SUCCESS)
+			return 0;
+
+	return pam_getenvlist (pamh);
+}
+
+#endif /* USE_PAM */

Reply via email to