summaryrefslogtreecommitdiffstats
path: root/usr.sbin/cron
diff options
context:
space:
mode:
authoryar <yar@FreeBSD.org>2007-06-17 17:25:53 +0000
committeryar <yar@FreeBSD.org>2007-06-17 17:25:53 +0000
commit333d04678de0f758b3332c024f321aa2f9b801b2 (patch)
tree5b2964d6813053eb898acc04fa43beede2e79239 /usr.sbin/cron
parent481992080252c6f1525eb50007ea8390bb08ea27 (diff)
downloadFreeBSD-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/cron')
-rw-r--r--usr.sbin/cron/cron/Makefile6
-rw-r--r--usr.sbin/cron/cron/cron.827
-rw-r--r--usr.sbin/cron/cron/cron.h1
-rw-r--r--usr.sbin/cron/cron/database.c4
-rw-r--r--usr.sbin/cron/cron/do_command.c46
-rw-r--r--usr.sbin/cron/lib/Makefile2
-rw-r--r--usr.sbin/cron/lib/entry.c2
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;
OpenPOWER on IntegriCloud