diff options
author | yar <yar@FreeBSD.org> | 2007-06-17 17:25:53 +0000 |
---|---|---|
committer | yar <yar@FreeBSD.org> | 2007-06-17 17:25:53 +0000 |
commit | 333d04678de0f758b3332c024f321aa2f9b801b2 (patch) | |
tree | 5b2964d6813053eb898acc04fa43beede2e79239 /usr.sbin | |
parent | 481992080252c6f1525eb50007ea8390bb08ea27 (diff) | |
download | FreeBSD-src-333d04678de0f758b3332c024f321aa2f9b801b2.zip FreeBSD-src-333d04678de0f758b3332c024f321aa2f9b801b2.tar.gz |
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
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/cron/cron/Makefile | 6 | ||||
-rw-r--r-- | usr.sbin/cron/cron/cron.8 | 27 | ||||
-rw-r--r-- | usr.sbin/cron/cron/cron.h | 1 | ||||
-rw-r--r-- | usr.sbin/cron/cron/database.c | 4 | ||||
-rw-r--r-- | usr.sbin/cron/cron/do_command.c | 46 | ||||
-rw-r--r-- | usr.sbin/cron/lib/Makefile | 2 | ||||
-rw-r--r-- | usr.sbin/cron/lib/entry.c | 2 |
7 files changed, 80 insertions, 8 deletions
diff --git a/usr.sbin/cron/cron/Makefile b/usr.sbin/cron/cron/Makefile index d578da7..92d3ee4 100644 --- a/usr.sbin/cron/cron/Makefile +++ b/usr.sbin/cron/cron/Makefile @@ -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> diff --git a/usr.sbin/cron/cron/cron.8 b/usr.sbin/cron/cron/cron.8 index eba639d..b0d1a91 100644 --- a/usr.sbin/cron/cron/cron.8 +++ b/usr.sbin/cron/cron/cron.8 @@ -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 diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h index 249bec9..6277719 100644 --- a/usr.sbin/cron/cron/cron.h +++ b/usr.sbin/cron/cron/cron.h @@ -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. diff --git a/usr.sbin/cron/cron/database.c b/usr.sbin/cron/cron/database.c index 9ebf75d..6c364c9 100644 --- a/usr.sbin/cron/cron/database.c +++ b/usr.sbin/cron/cron/database.c @@ -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"); diff --git a/usr.sbin/cron/cron/do_command.c b/usr.sbin/cron/cron/do_command.c index cb0bf02..afc8317 100644 --- a/usr.sbin/cron/cron/do_command.c +++ b/usr.sbin/cron/cron/do_command.c @@ -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 diff --git a/usr.sbin/cron/lib/Makefile b/usr.sbin/cron/lib/Makefile index d11c511..296ebd0 100644 --- a/usr.sbin/cron/lib/Makefile +++ b/usr.sbin/cron/lib/Makefile @@ -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> diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c index 7269152..33ace49 100644 --- a/usr.sbin/cron/lib/entry.c +++ b/usr.sbin/cron/lib/entry.c @@ -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; |