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
This commit is contained in:
parent
e6efe4733c
commit
065057e635
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -86,14 +86,14 @@ MANPAGES = bitstring.3 crontab.5 crontab.1 cron.8 putman.sh
|
|||
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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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/12/24 19:30:46 christos Exp $");
|
|||
|
||||
#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 stat *st, int sys, cron_db *new_db,
|
|||
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);
|
||||
process_crontab("root", "*system*", SYSCRONTAB,
|
||||
&syscron_stat, &new_db, old_db);
|
||||
|
||||
if (crond_stat.st_mtime)
|
||||
process_dir(CROND_DIR, &crond_stat, 1, &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 char *fname, const char *tabname,
|
|||
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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 passwd *);
|
|||
#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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
|
@ -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 *type, struct passwd *pw) {
|
|||
_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);
|
||||
|
|
Loading…
Reference in New Issue