mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-28 11:57:28 +00:00
Add PAM support to cron(8). Now cron(8) will skip commands scheduled
by unavailable accounts, e.g., those locked, expired, not allowed in at the moment by nologin(5), or whatever, depending on cron's pam.conf(5). This applies to personal crontabs only, /etc/crontab is unaffected. In other words, now the account management policy will apply to commands scheduled by users via crontab(1) so that a user can no longer use cron(8) to set up a delayed backdoor and run commands during periods when the admin doesn't want him to. The PAM check is done just before running a command, not when loading a crontab, because accounts can get locked, expired, and re-enabled any time with no changes to their crontabs. E.g., imagine that you provide a system with payed access, or better a cluster of such systems with centralized account management via PAM. When a user pays for some days of access, you set his expire field respectively. If the account expires before its owner pays more, its crontab commands won't run until the next payment is made. Then it'll be enough to set the expire field in future for the commands to run again. And so on. Document this change in the cron(8) manpage, which includes adding a FILES section and touching the document date. X-Security: should benefit as users have access to cron(8) by default
This commit is contained in:
parent
5e521fcd2e
commit
997c6eefd8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=170890
@ -4,6 +4,7 @@ NO_OBJ=
|
||||
|
||||
FILES= README \
|
||||
atrun \
|
||||
cron \
|
||||
ftpd \
|
||||
gdm \
|
||||
imap \
|
||||
|
9
etc/pam.d/cron
Normal file
9
etc/pam.d/cron
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
# PAM configuration for the "cron" service
|
||||
#
|
||||
|
||||
# account
|
||||
account required pam_nologin.so
|
||||
account required pam_unix.so
|
@ -4,9 +4,9 @@ PROG= cron
|
||||
MAN= cron.8
|
||||
SRCS= cron.c database.c do_command.c job.c user.c popen.c
|
||||
|
||||
CFLAGS+= -DLOGIN_CAP
|
||||
CFLAGS+= -DLOGIN_CAP -DPAM
|
||||
|
||||
DPADD= ${LIBCRON} ${LIBUTIL}
|
||||
LDADD= ${LIBCRON} -lutil
|
||||
DPADD= ${LIBCRON} ${LIBPAM} ${LIBUTIL}
|
||||
LDADD= ${LIBCRON} -lpam -lutil
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -17,7 +17,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd December 20, 1993
|
||||
.Dd June 17, 2007
|
||||
.Dt CRON 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -53,11 +53,21 @@ utility also searches for
|
||||
.Pa /etc/crontab
|
||||
which is in a different format (see
|
||||
.Xr crontab 5 ) .
|
||||
.Pp
|
||||
The
|
||||
.Nm
|
||||
utility
|
||||
then wakes up every minute, examining all stored crontabs, checking each
|
||||
command to see if it should be run in the current minute.
|
||||
Before running a command from a per-account crontab file,
|
||||
.Nm
|
||||
checks the status of the account with
|
||||
.Xr pam 3
|
||||
and skips the command if the account is unavailable,
|
||||
e.g., locked out or expired.
|
||||
Commands from
|
||||
.Pa /etc/crontab
|
||||
bypass this check.
|
||||
When executing
|
||||
commands, any output is mailed to the owner of the crontab (or to the user
|
||||
named in the
|
||||
@ -173,8 +183,21 @@ be verbose when iterating through the scheduling algorithms
|
||||
trace through the execution, but do not perform any actions
|
||||
.El
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width /etc/pam.d/cron -compact
|
||||
.It Pa /etc/crontab
|
||||
System crontab file
|
||||
.It Pa /etc/pam.d/cron
|
||||
.Xr pam.conf 5
|
||||
configuration file for
|
||||
.Nm
|
||||
.It Pa /var/cron/tabs
|
||||
Directory for personal crontab files
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr crontab 1 ,
|
||||
.Xr crontab 5
|
||||
.Xr pam 3 ,
|
||||
.Xr crontab 5 ,
|
||||
.Xr pam.conf 5
|
||||
.Sh AUTHORS
|
||||
.An Paul Vixie Aq paul@vix.com
|
||||
|
@ -76,6 +76,7 @@
|
||||
#define MAX_UNAME 20 /* max length of username, should be overkill */
|
||||
#define ROOT_UID 0 /* don't change this, it really must be root */
|
||||
#define ROOT_USER "root" /* ditto */
|
||||
#define SYS_NAME "*system*" /* magic owner name for system crontab */
|
||||
|
||||
/* NOTE: these correspond to DebugFlagNames,
|
||||
* defined below.
|
||||
|
@ -87,7 +87,7 @@ load_database(old_db)
|
||||
new_db.head = new_db.tail = NULL;
|
||||
|
||||
if (syscron_stat.st_mtime) {
|
||||
process_crontab("root", "*system*",
|
||||
process_crontab("root", SYS_NAME,
|
||||
SYSCRONTAB, &syscron_stat,
|
||||
&new_db, old_db);
|
||||
}
|
||||
@ -205,7 +205,7 @@ process_crontab(uname, fname, tabname, statbuf, new_db, old_db)
|
||||
int crontab_fd = OK - 1;
|
||||
user *u;
|
||||
|
||||
if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) {
|
||||
if (strcmp(fname, SYS_NAME) && !(pw = getpwnam(uname))) {
|
||||
/* file doesn't have a user in passwd file.
|
||||
*/
|
||||
log_it(fname, getpid(), "ORPHAN", "no passwd entry");
|
||||
|
@ -32,6 +32,10 @@ static const char rcsid[] =
|
||||
#if defined(LOGIN_CAP)
|
||||
# include <login_cap.h>
|
||||
#endif
|
||||
#ifdef PAM
|
||||
# include <security/pam_appl.h>
|
||||
# include <security/openpam.h>
|
||||
#endif
|
||||
|
||||
|
||||
static void child_process __P((entry *, user *)),
|
||||
@ -98,6 +102,48 @@ child_process(e, u)
|
||||
usernm = env_get("LOGNAME", e->envp);
|
||||
mailto = env_get("MAILTO", e->envp);
|
||||
|
||||
#ifdef PAM
|
||||
/* use PAM to see if the user's account is available,
|
||||
* i.e., not locked or expired or whatever. skip this
|
||||
* for system tasks from /etc/crontab -- they can run
|
||||
* as any user.
|
||||
*/
|
||||
if (strcmp(u->name, SYS_NAME)) { /* not equal */
|
||||
pam_handle_t *pamh = NULL;
|
||||
int pam_err;
|
||||
struct pam_conv pamc = {
|
||||
.conv = openpam_nullconv,
|
||||
.appdata_ptr = NULL
|
||||
};
|
||||
|
||||
Debug(DPROC, ("[%d] checking account with PAM\n", getpid()))
|
||||
|
||||
/* u->name keeps crontab owner name while LOGNAME is the name
|
||||
* of user to run command on behalf of. they should be the
|
||||
* same for a task from a per-user crontab.
|
||||
*/
|
||||
if (strcmp(u->name, usernm)) {
|
||||
log_it(usernm, getpid(), "username ambiguity", u->name);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
pam_err = pam_start("cron", usernm, &pamc, &pamh);
|
||||
if (pam_err != PAM_SUCCESS) {
|
||||
log_it("CRON", getpid(), "error", "can't start PAM");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
pam_err = pam_acct_mgmt(pamh, PAM_SILENT);
|
||||
/* Expired password shouldn't prevent the job from running. */
|
||||
if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) {
|
||||
log_it(usernm, getpid(), "USER", "account unavailable");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
pam_end(pamh, pam_err);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SIGCHLD
|
||||
/* our parent is watching for our death by catching SIGCHLD. we
|
||||
* do not care to watch for our children's deaths this way -- we
|
||||
|
@ -5,6 +5,6 @@ INTERNALLIB=
|
||||
SRCS= entry.c env.c misc.c
|
||||
|
||||
CFLAGS+= -I${.CURDIR}/../cron
|
||||
CFLAGS+= -DLOGIN_CAP
|
||||
CFLAGS+= -DLOGIN_CAP -DPAM
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -323,10 +323,12 @@ load_entry(file, error_func, pw, envp)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef PAM /* PAM takes care of account expiration by itself */
|
||||
if (pw->pw_expire && time(NULL) >= pw->pw_expire) {
|
||||
ecode = e_username;
|
||||
goto eof;
|
||||
}
|
||||
#endif /* !PAM */
|
||||
|
||||
e->uid = pw->pw_uid;
|
||||
e->gid = pw->pw_gid;
|
||||
|
Loading…
Reference in New Issue
Block a user