1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
From: Christian Kastner <ckk@kvr.at>
Date: Sat, 23 Mar 2019 09:41:38 +0100
Subject: Enforce maximum crontab line count
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. This was classified as a security issue with
CVE-2019-9705.
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.
Forwarded: no
Last-Update: 2019-03-23
---
cron.h | 4 ++++
crontab.c | 12 ++++++++----
user.c | 14 +++++++++++++-
3 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/cron.h b/cron.h
index 2fcc94d..2120367 100644
--- a/cron.h
+++ b/cron.h
@@ -68,6 +68,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 10000 /* 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 */
@@ -87,6 +88,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
@@ -112,6 +114,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)); \
diff --git a/crontab.c b/crontab.c
index 0c9e2d9..fae9fc2 100644
--- a/crontab.c
+++ b/crontab.c
@@ -45,9 +45,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
@@ -761,7 +758,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;
@@ -778,6 +775,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);
diff --git a/user.c b/user.c
index 38a8799..5226102 100644
--- a/user.c
+++ b/user.c
@@ -130,7 +130,19 @@ load_user(crontab_fd, pw, name)
}
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);
|