Commit 26814a26 authored by Christian Kastner's avatar Christian Kastner

Enforce maximum crontab line count of 1000

As any user can create a crontab that is read by the cron daemon, it is
possible for a user to cause a DoS via memory exhaustion by creating an
excessivly large crontab.

As a measure to prevent this, limit the size of individual crontab files
to 1000 lines.

While it is still technically possible for a user to create a larger crontab
(for example, by creating a single, very long comment), this should not affect
the daemon, as it simply skips over comments.

For crontab entries (for which the daemon allocates memory), the maximum
command length is already limited to 998 characters, so these allocations are
already kept in check.
parent f2525567
......@@ -82,6 +82,7 @@
#define MAX_COMMAND 1000 /* max length of internally generated cmd */
#define MAX_TEMPSTR 1000 /* max length of envvar=value\0 strings */
#define MAX_ENVSTR MAX_TEMPSTR /* DO NOT change - buffer overruns otherwise */
#define MAX_TAB_LINES 1000 /* max length of crontabs */
#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 */
......@@ -101,6 +102,7 @@
#define CRON_TAB(u) "%s/%s", SPOOL_DIR, u
#define REG register
#define PPC_NULL ((char **)NULL)
#define NHEADER_LINES 3
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
......@@ -126,6 +128,8 @@
;
#endif /* DEBUGGING */
#define Stringify_(x) #x
#define Stringify(x) Stringify_(x)
#define MkLower(ch) (isupper(ch) ? tolower(ch) : ch)
#define MkUpper(ch) (islower(ch) ? toupper(ch) : ch)
#define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \
......
......@@ -46,9 +46,6 @@ static char rcsid[] = "$Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $";
#endif
#define NHEADER_LINES 3
enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace };
#if DEBUGGING
......@@ -879,7 +876,7 @@ replace_cmd() {
*/
Set_LineNum(1 - NHEADER_LINES)
CheckErrorCount = 0; eof = FALSE;
while (!CheckErrorCount && !eof) {
while (!CheckErrorCount && !eof && LineNumber < MAX_TAB_LINES + 2) {
switch (load_env(envstr, tmp)) {
case ERR:
eof = TRUE;
......@@ -896,6 +893,13 @@ replace_cmd() {
}
}
if (LineNumber >= MAX_TAB_LINES + 2) {
fprintf(stderr, "crontab is too long; maximum number of lines "
"is %d.\n", MAX_TAB_LINES);
fclose(tmp); unlink(tn);
return (-1);
}
if (CheckErrorCount != 0) {
fprintf(stderr, "errors in crontab file, can't install.\n");
fclose(tmp); unlink(tn);
......
......@@ -242,6 +242,7 @@ load_user(crontab_fd, pw, uname, fname, tabname)
/*
* load the crontab
*/
Set_LineNum(1)
do {
status = load_env(envstr, file);
switch (status) {
......@@ -287,7 +288,19 @@ load_user(crontab_fd, pw, uname, fname, tabname)
}
break;
}
} while (status >= OK);
/* When counting lines, ignore the user-hidden header part, and account
* for idiosyncrasies of LineNumber manipulation
*/
} while (status >= OK && LineNumber < MAX_TAB_LINES + NHEADER_LINES + 2);
if (LineNumber >= MAX_TAB_LINES + NHEADER_LINES + 2) {
log_it(fname, getpid(), "ERROR", "crontab must not be longer "
"than " Stringify(MAX_TAB_LINES) " lines, "
"this crontab file will be ignored");
free_user(u);
u = NULL;
goto done;
}
done:
env_free(envp);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment