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:
christos 2017-06-09 17:36:29 +00:00
parent e6efe4733c
commit 065057e635
13 changed files with 368 additions and 111 deletions

View File

@ -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

View File

@ -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

View File

@ -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

34
external/bsd/cron/dist/closeall.c vendored Normal file
View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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
*/

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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;

121
external/bsd/cron/dist/pam_auth.c vendored Normal file
View File

@ -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 */

View File

@ -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);