Package: release.debian.org Severity: normal User: release.debian....@packages.debian.org Usertags: unblock
Dear Release Team, I'd like to request an unblock for cron/3.0pl1-133. The notable changes are smallish, and mainly concern security fixes that have been assigned CVE-2019-9704, -9705, and -9706 in the meantime. One of these fixes (limiting crontabs to 1000 lines) required a debian/NEWS entry. However, there are a number of insignificant changes that make the debdiff quite large. I pushed these without pre-approval because I erroneously assumed that anything uploaded before the hard freeze may still transition in 10 days without explicit unblock, and because, well, they are insignificant. A great deal of these changes are trivial (eg man page fixes) or completely irrelevant (eg whitespace fixes). It may sound odd to push irrelevant changes, but I needed for the upcoming conversion to source format 3.0 (quilt), which I'd like to see in stretch. This conversion is greatly facilitated by reducing the diff to the original upstream, and reducing the accumulated noise so far. (with close to 70 patches, any reduction in noise counts greatly...) I've attached a debdiff -w, and a debdiff -w --exclude {MANPAGES}, but offer the following comments on all commits affecting this release as a guide: Notable changes (all documented in d/changelog): Security: https://salsa.debian.org/debian/cron/commit/13d44ca https://salsa.debian.org/debian/cron/commit/40791b9 https://salsa.debian.org/debian/cron/commit/f252556 https://salsa.debian.org/debian/cron/commit/26814a2 Add NEWS entry crontab line limit: https://salsa.debian.org/debian/cron/commit/70c6730 Move crond.reboot from /var/run to /run https://salsa.debian.org/debian/cron/commit/0222c88 https://salsa.debian.org/debian/cron/commit/8beb02d Man page fixes: https://salsa.debian.org/debian/cron/commit/9853021 https://salsa.debian.org/debian/cron/commit/286dde8 https://salsa.debian.org/debian/cron/commit/90a23a4 Fix the daemon 'x' option: https://salsa.debian.org/debian/cron/commit/39591a2 Whitespace cleanups https://salsa.debian.org/debian/cron/commit/6b28e6a https://salsa.debian.org/debian/cron/commit/56d0c88 Trivial changes (commit messages elaborate) https://salsa.debian.org/debian/cron/commit/391cc96 https://salsa.debian.org/debian/cron/commit/2eeba30 https://salsa.debian.org/debian/cron/commit/d7d3153 https://salsa.debian.org/debian/cron/commit/867936d https://salsa.debian.org/debian/cron/commit/d7d3153 https://salsa.debian.org/debian/cron/commit/55fa0e6 https://salsa.debian.org/debian/cron/commit/f521ca9 https://salsa.debian.org/debian/cron/commit/45fa23e With regards to the security fixes, uploads for jessie and stretch are being or have been prepared. Regards, Christian unblock cron/3.0pl1-133
diff -wu cron-3.0pl1/Makefile cron-3.0pl1/Makefile --- cron-3.0pl1/Makefile +++ cron-3.0pl1/Makefile @@ -57,8 +57,8 @@ #<<need getopt()>> LIBS = $(PAM_LIBS) $(SELINUX_LIBS) $(AUDIT_LIBS) #<<optimize or debug?>> -OPTIM = -O2 -#OPTIM = -g +#OPTIM = -O +OPTIM = -g #<<ATT or BSD or POSIX?>> # (ATT untested) #COMPAT = -DATT @@ -73,17 +73,14 @@ #<<manifest defines>> # Allow override from command line DEBUG_DEFS ?= -DDEBUGGING=0 -# The -DUSE_SIGCHLD is needed for the Alpha port -DEFS = -DDEBIAN -DUSE_SIGCHLD $(DEBUG_DEFS) $(PAM_DEFS) $(SELINUX_DEFS) $(AUDIT_DEFS) +DEFS = $(DEBUG_DEFS) $(PAM_DEFS) $(SELINUX_DEFS) $(AUDIT_DEFS) #(SGI IRIX systems need this) #DEFS = -D_BSD_SIGNALS -Dconst= #<<the name of the BSD-like install program>> #INSTALL = installbsd -INSTALL = install -s +INSTALL = install #<<any special load flags>> -# LDFLAGS = -s -# Let install do the strip - +#LDFLAGS = #################################### end configurable stuff SHELL = /bin/sh diff -wu cron-3.0pl1/compat.h cron-3.0pl1/compat.h --- cron-3.0pl1/compat.h +++ cron-3.0pl1/compat.h @@ -62,8 +62,8 @@ #endif #ifndef POSIX -# if (BSD >= 199103) || defined(__linux__) || defined(__GNU__) || defined(ultrix) ||\ - defined(AIX) ||\ defined(HPUX) || defined(CONVEX) || defined(IRIX) || defined(__GLIBC__) +# if (BSD >= 199103) || defined(__linux__) || defined(ultrix) || defined(AIX) ||\ + defined(HPUX) || defined(CONVEX) || defined(IRIX) # define POSIX # endif #endif @@ -76,17 +76,17 @@ /*****************************************************************/ -#if !defined(BSD) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux__) && !defined(__GNU__) +#if !defined(BSD) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux__) && !defined(__GLIBC__) # define NEED_VFORK #endif #if (!defined(BSD) || (BSD < 198902)) && !defined(__linux__) && \ - !defined(IRIX) && !defined(NeXT) && !defined(HPUX) && !defined(__GNU__) && !defined(__GLIBC__) + !defined(IRIX) && !defined(NeXT) && !defined(HPUX) && !defined(__GLIBC__) # define NEED_STRCASECMP #endif #if (!defined(BSD) || (BSD < 198911)) && !defined(__linux__) &&\ - !defined(IRIX) && !defined(UNICOS) && !defined(HPUX) && !defined(__GNU__) && !defined(__GLIBC__) + !defined(IRIX) && !defined(UNICOS) && !defined(HPUX) && !defined(__GLIBC__) # define NEED_STRDUP #endif @@ -102,7 +102,7 @@ # define NEED_SETSID #endif -#if (defined(POSIX) && !defined(BSD)) && !defined(__linux__) && !defined(__GNU__) && !defined(__GLIBC__) +#if (defined(POSIX) && !defined(BSD)) && !defined(__linux__) && !defined(__GLIBC__) # define NEED_GETDTABLESIZE #endif @@ -110,11 +110,11 @@ # define HAVE_SAVED_UIDS #endif -#if !defined(ATT) && !defined(__linux__) && !defined(__GNU__) && !defined(IRIX) && !defined(UNICOS) && !defined(__GLIBC__) +#if (!defined(ATT) && !defined(IRIX) && !defined(UNICOS)) || defined(POSIX) # define USE_SIGCHLD #endif -#if !defined(AIX) && !defined(UNICOS) && !defined(DEBIAN) +#if !defined(AIX) && !defined(UNICOS) && !defined(POSIX) # define SYS_TIME_H 1 #else # define SYS_TIME_H 0 diff -wu cron-3.0pl1/config.h cron-3.0pl1/config.h --- cron-3.0pl1/config.h +++ cron-3.0pl1/config.h @@ -43,10 +43,10 @@ */ #define MAILCMD _PATH_SENDMAIL /*-*/ -/* #define MAILARGS "%s -i -FCronDaemon -odi -oem %s" /*-*/ -#define MAILARGS "%s -i -FCronDaemon -B8BITMIME -oem %s" /*-*/ - /* -i = don't terminate on "." by itself - * -Fx = set full-name of sender +#define MAILARGS "%s -FCronDaemon -i -B8BITMIME -oem %s" /*-*/ + /* -Fx = set full-name of sender + * -i = don't terminate on "." by itself + * -B8BITMIME = 8-bit processing * -odi = Option Deliverymode Interactive * -oem = Option Errors Mailedtosender * -t = read recipient from header of message @@ -55,15 +55,15 @@ * by joe user. --okir */ -/* #define MAILCMD "/bin/mail" -*/ -/* #define MAILARGS "%s -d %s" -*/ +/* #define MAILCMD "/bin/mail" /*-*/ +/* #define MAILARGS "%s -d %s" /*-*/ /* -d = undocumented but common flag: deliver locally? */ -/* #define MAILCMD "/usr/mmdf/bin/submit" -*/ -/* #define MAILARGS "%s -mlrxto %s" -*/ +/* #define MAILCMD "/usr/mmdf/bin/submit" /*-*/ +/* #define MAILARGS "%s -mlrxto %s" /*-*/ -/* #define MAIL_DATE -*/ +/* #define MAIL_DATE /*-*/ /* should we include an ersatz Date: header in * generated mail? if you are using sendmail * for MAILCMD, it is better to let sendmail @@ -74,7 +74,7 @@ * defined but neither exists, should crontab(1) be * usable only by root? */ -/*#define ALLOW_ONLY_ROOT -*/ +/*#define ALLOW_ONLY_ROOT /*-*/ /* if you want to use syslog(3) instead of appending * to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define diff -wu cron-3.0pl1/cron.8 cron-3.0pl1/cron.8 --- cron-3.0pl1/cron.8 +++ cron-3.0pl1/cron.8 @@ -23,9 +23,9 @@ cron \- daemon to execute scheduled commands (Vixie Cron) .SH SYNOPSIS cron -.RB [ -f ] -.RB [ -l ] -.RB [ -L +.RB [ \-f ] +.RB [ \-l ] +.RB [ \-L .IR loglevel ] .SH DESCRIPTION .I cron @@ -33,19 +33,19 @@ runlevels. .SH OPTIONS .TP 8 -.B -f +.B \-f Stay in foreground mode, don't daemonize. .TP -.B -l +.B \-l Enable LSB compliant names for /etc/cron.d files. This setting, however, does not affect the parsing of files under /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly or /etc/cron.monthly. .TP -.B -n +.B \-n Include the FQDN in the subject when sending mails. By default, cron will abbreviate the hostname. .TP -.B -L loglevel +.B \-L loglevel Tell cron what to log about \fBjobs\fR (errors are logged regardless of this value) as the sum of the following values: .br @@ -60,9 +60,11 @@ will log the process number of all cron jobs .RE .IP -The default is to log the start of all jobs (1). Logging will be disabled -if \fIlevels\fR is set to zero (0). A value of fifteen (15) will select all -options. +The default is to log the start of all jobs (1). +Logging will be disabled if +.I levels +is set to zero (0). +A value of fifteen (15) will select all options. .SH NOTES .PP .I cron @@ -88,8 +90,9 @@ .I cron reads the files in the /etc/cron.d directory. .I cron -treats the files in /etc/cron.d as in the same way as the /etc/crontab file (they -follow the special format of that file, i.e. they include the +treats the files in /etc/cron.d as in the same way as the /etc/crontab +file (they follow the special format of that file, +i.e.\& they include the .I user field). However, they are independent of /etc/crontab: they do not, for example, inherit environment variable settings from it. This change is @@ -98,7 +101,8 @@ below. Like /etc/crontab, the files in the /etc/cron.d directory are -monitored for changes. In general, the system administrator should not use /etc/cron.d/, +monitored for changes. +In general, the system administrator should not use /etc/cron.d/, but use the standard system crontab /etc/crontab. /etc/crontab and the files in /etc/cron.d must be owned by root, and must not @@ -127,11 +131,11 @@ .I cron checks each minute to see if its spool directory's modtime (or the modtime on the -.IR /etc/crontab +.I /etc/crontab file) has changed, and if it has, .I cron -will then examine the modtime on all crontabs files and reload those which have +will then examine the modtime on all crontabs and reload those which have changed. Thus .I cron need not be restarted whenever a crontab file is modified. Note that the @@ -177,8 +181,7 @@ .B NOT affect the environment of tasks running under cron. For more information on how to modify the environment of tasks, consult -.IR crontab(5) -\. +.IR crontab (5). .PP The daemon will use, if present, the definition from .I /etc/timezone @@ -224,7 +227,8 @@ The default system-wide crontab contains four tasks: run every hour, every day, every week and every month. Each of these tasks will execute .B run-parts -providing each one of the directories as an argument. These tasks are disabled if +providing each one of the directories as an argument. +These tasks are disabled if .B anacron is installed (except for the hourly task) to prevent conflicts between both daemons. @@ -242,8 +246,8 @@ For example, any file containing dots will be ignored. This is done to prevent cron from running any of the files that are left by the Debian package management system when handling files in -/etc/cron.d/ as configuration files (i.e. files ending in .dpkg-dist, .dpkg-orig, -and .dpkg-new). +/etc/cron.d/ as configuration files (i.e.\& files ending in +\&.dpkg-dist, \&.dpkg-orig, and \&.dpkg-new). This feature can be used by system administrators and packages to include tasks that will be run at defined intervals. Files created by packages in these @@ -254,31 +258,31 @@ .I cron daemon itself, which handles this location as the system-wide crontab spool. This directory can contain any file defining tasks following the format -used in /etc/crontab, i.e. unlike the user cron spool, these files must +used in /etc/crontab, i.e.\& unlike the user cron spool, these files must provide the username to run the task as in the task definition. Files in this directory have to be owned by root, do not need to be executable (they are configuration files, just like /etc/crontab) and must conform to the same naming convention as used by -.IR run-parts(8) : +.IR run-parts "(8) :" they must consist solely of upper- and lower-case letters, digits, underscores, and hyphens. This means that they .B cannot contain any dots. If the -.B -l +.B \-l option is specified to .I cron (this option can be setup through /etc/default/cron, see below), then they must conform to the LSB namespace specification, exactly as in the -.B --lsbsysinit +.B \-\-lsbsysinit option in .IR run-parts . -The intended purpose -of this feature is to allow packages that require -finer control of their scheduling than the /etc/cron.{hourly,daily,weekly,monthly} +The intended purpose of this feature is to allow packages that require +finer control of their scheduling than the +/etc/cron.{hourly,daily,weekly,monthly} directories to add a crontab file to /etc/cron.d. Such files should be named after the package that supplies them. diff -wu cron-3.0pl1/cron.c cron-3.0pl1/cron.c --- cron-3.0pl1/cron.c +++ cron-3.0pl1/cron.c @@ -26,6 +26,7 @@ #include "cron.h" #include <signal.h> +#include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <libgen.h> @@ -49,8 +50,9 @@ char **dflags; fprintf(stderr, "usage: %s [-x [", ProgramName); - for(dflags = DebugFlagNames; *dflags; dflags++) + for (dflags = DebugFlagNames; *dflags; dflags++) { fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "]"); + } fprintf(stderr, "]\n"); #else fprintf(stderr, "usage: %s\n", ProgramName); @@ -83,9 +85,6 @@ #endif (void) signal(SIGHUP, sighup_handler); - /* Reopen stdin in case some idiot closed it before starting - us - it will only be closed, but not having it open here - screws up other things that will be opened */ if (fdopen(0,"r") == NULL) { (void) open("dev/null", 0); } @@ -145,11 +144,8 @@ database.tail = NULL; database.sys_mtime = (time_t) 0; database.user_mtime = (time_t) 0; -#ifdef DEBIAN database.sysd_mtime = (time_t) 0; -#endif load_database(&database); - set_time(TRUE); run_reboot_jobs(&database); timeRunning = virtualTime = clockTime; @@ -273,10 +269,6 @@ } } -#ifdef DEBIAN -#include <sys/stat.h> -#include <fcntl.h> -#endif static void run_reboot_jobs(db) @@ -285,8 +277,7 @@ register user *u; register entry *e; int rbfd; -#ifdef DEBIAN -#define REBOOT_FILE "/var/run/crond.reboot" + /* Run on actual reboot, rather than cron restart */ if (access(REBOOT_FILE, F_OK) == 0) { /* File exists, return */ @@ -303,12 +294,7 @@ close(rbfd); log_it("CRON", getpid(),"INFO", "Running @reboot jobs"); } - - - Debug(DMISC, ("[%d], Debian running reboot jobs\n",getpid())); - -#endif - Debug(DMISC, ("[%d], vixie running reboot jobs\n", getpid())); + Debug(DMISC, ("[%d], running reboot jobs\n", getpid())); for (u = db->head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { if (e->flags & WHEN_REBOOT) { @@ -418,7 +404,7 @@ #ifdef USE_SIGCHLD static void -sigchld_handler(x) { +sigchld_handler(int x) { int save_errno = errno; WAIT_T waiter; PID_T pid; @@ -452,7 +438,7 @@ static void -sighup_handler(x) { +sighup_handler(int x) { log_close(); /* we should use sigaction for proper signal blocking as this @@ -461,6 +447,12 @@ } +#if DEBUGGING +const char *getoptarg = "flL:nx:"; +#else +const char *getoptarg = "flL:n"; +#endif + static void parse_args(argc, argv) int argc; @@ -468,31 +460,33 @@ { int argch; - log_level = 1; stay_foreground = 0; lsbsysinit_mode = 0; + log_level = 1; fqdn_in_subject = 0; - while (EOF != (argch = getopt(argc, argv, "lfnx:L:"))) { + while (EOF != (argch = getopt(argc, argv, getoptarg))) { switch (argch) { default: usage(); case 'f': stay_foreground = 1; break; - case 'x': - if (!set_debug_flags(optarg)) - usage(); - break; case 'l': lsbsysinit_mode = 1; break; + case 'L': + log_level = atoi(optarg); + break; case 'n': fqdn_in_subject = 1; break; - case 'L': - log_level = atoi(optarg); +#if DEBUGGING + case 'x': + if (!set_debug_flags(optarg)) + usage(); break; +#endif } } } diff -wu cron-3.0pl1/cron.h cron-3.0pl1/cron.h --- cron-3.0pl1/cron.h +++ cron-3.0pl1/cron.h @@ -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)); \ @@ -209,9 +213,7 @@ user *head, *tail; /* links */ time_t user_mtime; /* last modtime on spooldir */ time_t sys_mtime; /* last modtime on system crontab */ -#ifdef DEBIAN time_t sysd_mtime; /* last modtime on system crondir */ -#endif } cron_db; typedef struct _orphan { @@ -263,7 +265,7 @@ **env_set __P((char **, char *)); user *load_user __P((int, struct passwd *, char *, char *, char *)), - *find_user __P((cron_db *, char *)); + *find_user __P((cron_db *, const char *)); entry *load_entry __P((FILE *, void (*)(), struct passwd *, char **)); @@ -305,8 +307,9 @@ int stay_foreground; int lsbsysinit_mode; +int log_level; int fqdn_in_subject; -int log_level = 1; + char cron_default_mail_charset[MAX_ENVSTR] = ""; # if DEBUGGING @@ -322,8 +325,8 @@ *DowNames[], *ProgramName; extern int lsbsysinit_mode; -extern int fqdn_in_subject; extern int log_level; +extern int fqdn_in_subject; extern int LineNumber; extern time_t StartTime; extern time_min timeRunning; diff -wu cron-3.0pl1/crontab.1 cron-3.0pl1/crontab.1 --- cron-3.0pl1/crontab.1 +++ cron-3.0pl1/crontab.1 @@ -133,6 +133,14 @@ /var/spool/cron/crontabs .fi .PP +The files +.I /etc/cron.allow +and +.I /etc/cron.deny +if, they exist, must be either world-readable, or readable by group +``crontab''. If they are not, then cron will deny access to all users until the +permissions are fixed. +.PP There is one file for each user's crontab under the /var/spool/cron/crontabs directory. Users are not allowed to edit the files under that directory directly to ensure that only users allowed by the system to run periodic tasks @@ -157,7 +165,6 @@ last entry in a crontab is missing the newline, cron will consider the crontab (at least partially) broken and refuse to install it. -.SH DIAGNOSTICS The files under .I /var/spool/cron/crontabs diff -wu cron-3.0pl1/crontab.5 cron-3.0pl1/crontab.5 --- cron-3.0pl1/crontab.5 +++ cron-3.0pl1/crontab.5 @@ -44,7 +44,9 @@ settings will affect only the cron commands below them in the file. An environment setting is of the form, .PP +.in +4n name = value +.in .PP where the spaces around the equal-sign (=) are optional, and any subsequent non-leading spaces in @@ -56,32 +58,35 @@ string may be placed in quotes (single or double, but matching) to preserve leading or trailing blanks. To define an empty variable, quotes .B must -be used. The +be used. +.PP +The .I value string is .B not -parsed for environmental substitutions or replacement of variables, thus lines -like +parsed for environmental substitutions or replacement of variables or +tilde(~) expansion, thus lines like .PP +.in +4n +.nf PATH = $HOME/bin:$PATH +PATH = ~/bin:/usr/bin:/bin +.fi +.in .PP will not work as you might expect. And neither will this work .PP +.in +4n +.nf A=1 B=2 C=$A $B +.fi +.in .PP There will not be any substitution for the defined variables in the last value. .PP -An alternative for setting up the commands path is using the fact that -many shells will treat the tilde(~) as substitution of $HOME, so if you use -.I bash -for your tasks you can use this: -.PP - SHELL=/bin/bash - PATH=~/bin:/usr/bin/:/bin -.PP Several environment variables are set up automatically by the .IR cron (8) daemon. @@ -105,7 +110,7 @@ On the Debian GNU/Linux system, cron supports the .B pam_env module, and loads the environment specified by -.IR /etc/environment +.I /etc/environment and .IR /etc/security/pam_env.conf . It also reads locale information from @@ -121,8 +126,9 @@ "text/plain" with the "charset=" parameter set to the charmap / codeset of the locale in which .IR crond (8) -is started up - i.e. either the default system locale, if no LC_* environment -variables are set, or the locale specified by the LC_* environment variables +is started up \(en i.e.\& either the default system locale, +if no LC_* environment variables are set, or the locale specified by +the LC_* environment variables ( see .IR locale (7) ). You can use different character encodings for mailed cron job output by @@ -131,7 +137,7 @@ .PP The format of a cron command is very much the V7 standard, with a number of upward-compatible extensions. Each line has five time and date fields, -followed by a command, followed by a newline character ('\\n'). +followed by a command, followed by a newline character ('\en'). The system crontab (/etc/crontab) uses the same format, except that the username for the command is specified after the time and date fields and before the command. The fields may be separated @@ -153,30 +159,30 @@ .br ----- -------------- .br -minute 0-59 +minute 0\(en59 .br -hour 0-23 +hour 0\(en23 .br -day of month 1-31 +day of month 1\(en31 .br -month 1-12 (or names, see below) +month 1\(en12 (or names, see below) .br -day of week 0-7 (0 or 7 is Sun, or use names) +day of week 0\(en7 (0 or 7 is Sun, or use names) .br .PP A field may be an asterisk (*), which always stands for ``first\-last''. .PP Ranges of numbers are allowed. Ranges are two numbers separated with a hyphen. The specified range is inclusive. For example, -8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10 +8\(en11 for an ``hours'' entry specifies execution at hours 8, 9, 10 and 11. .PP Lists are allowed. A list is a set of numbers (or ranges) -separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''. +separated by commas. Examples: ``1,2,5,9'', ``0\(en4,8\(en12''. .PP Step values can be used in conjunction with ranges. Following a range with ``/<number>'' specifies skips of the number's value -through the range. For example, ``0-23/2'' can be used in the hours +through the range. For example, ``0\(en23/2'' can be used in the hours field to specify command execution every other hour (the alternative in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are also permitted after an asterisk, so if you want to say ``every two @@ -193,10 +199,10 @@ character, will be executed by /bin/sh or by the shell specified in the SHELL variable of the crontab file. Percent-signs (%) in the command, unless escaped with backslash -(\\), will be changed into newline characters, and all data +(\e), will be changed into newline characters, and all data after the first % will be sent to the command as standard input. There is no way to split a single command line onto multiple -lines, like the shell's trailing "\\". +lines, like the shell's trailing "\e". .PP Note: The day of a command's execution can be specified by two fields \(em day of month, and day of week. If both fields are @@ -254,16 +260,16 @@ # # run five minutes after midnight, every day 5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1 -# run at 2:15pm on the first of every month -- output mailed to paul +# run at 2:15pm on the first of every month \(em output mailed to paul 15 14 1 * * $HOME/bin/monthly # run at 10 pm on weekdays, annoy Joe -0 22 * * 1-5 mail \-s "It's 10pm" joe%Joe,%%Where are your kids?% -23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday" +0 22 * * 1\(en5 mail \-s "It's 10pm" joe%Joe,%%Where are your kids?% +23 0\(en23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday" 5 4 * * sun echo "run at 5 after 4 every Sunday" 0 */4 1 * mon echo "run every 4th hour on the 1st and on every Monday" 0 0 */2 * sun echo "run at midn on every Sunday that's an uneven date" # Run on every second Saturday of the month -0 4 8-14 * * test $(date +\\%u) \-eq 6 && echo "2nd Saturday" +0 4 8\(en14 * * test $(date +\e%u) \-eq 6 && echo "2nd Saturday" .fi .PP @@ -323,10 +329,11 @@ When specifying day of week, both day 0 and day 7 will be considered Sunday. BSD and AT&T seem to disagree about this. .PP -Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would -be rejected by AT&T or BSD cron -- they want to see "1-3" or "7,8,9" ONLY. +Lists and ranges are allowed to co-exist in the same field. +"1\(en3,7\(en9" would be rejected by AT&T or BSD cron \(em they want +to see "1\(en3" or "7,8,9" ONLY. .PP -Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9". +Ranges can include "steps", so "1\(en9/2" is the same as "1,3,5,7,9". .PP Months or days of the week can be specified by name. .PP @@ -381,15 +388,15 @@ the following wrapper code: .nf -0 4 * * Sat [ "$(date +\\%e)" = "$(LANG=C ncal | sed -n 's/^Sa .* \\([0-9]\\+\\) *$/\\1/p')" ] && echo "Last Saturday" && program_to_run +0 4 * * Sat [ "$(date +\e%e)" = "$(LANG=C ncal | sed \-n 's/^Sa .* \e([0\(en9]\e+\e) *$/\ex1/p')" ] && echo "Last Saturday" && program_to_run .fi .SH DIAGNOSTICS cron requires that each entry in a crontab end in a newline character. If the -last entry in a crontab is missing a newline (i.e. terminated by EOF), cron will -consider the crontab (at least partially) broken. A warning will be written to -syslog. +last entry in a crontab is missing a newline (i.e.\& terminated by EOF), +cron will consider the crontab (at least partially) broken. +A warning will be written to syslog. .SH AUTHOR Paul Vixie <p...@vix.com> is the author of diff -wu cron-3.0pl1/crontab.c cron-3.0pl1/crontab.c --- cron-3.0pl1/crontab.c +++ cron-3.0pl1/crontab.c @@ -46,8 +46,6 @@ #endif -#define NHEADER_LINES 3 - enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; #if DEBUGGING @@ -155,10 +153,9 @@ #if DEBUGGING -char *getoptarg = "u:lerix:"; +const char *getoptarg = "u:lerix:"; #else -char *getoptarg = "u:leri"; +const char *getoptarg = "u:leri"; #endif - static void parse_args(argc, argv) @@ -189,7 +186,6 @@ case 'x': if (!set_debug_flags(optarg)) usage("bad debug option"); - usage("unrecognized option"); break; #endif case 'u': @@ -302,10 +298,8 @@ char n[MAX_FNAME]; FILE *f; int ch; -#ifdef DEBIAN int x; char *ctnh; -#endif log_it(RealUser, Pid, "LIST", User); (void) snprintf(n, MAX_FNAME, CRON_TAB(User)); @@ -321,30 +315,33 @@ /* file is open. copy to stdout, close. */ Set_LineNum(1) -#ifdef DEBIAN - /* DEBIAN: Don't list header lines unless CRONTAB_NOHEADER is - 'N'. */ - /* ignore the top few comments since we probably put them there. + + /* Don't list header lines unless CRONTAB_NOHEADER is 'N'. + * + * ignore the top few comments since we probably put them there. */ - if (!(ctnh = getenv("CRONTAB_NOHEADER")) || - toupper(*ctnh) != 'N') - { + if (!(ctnh = getenv("CRONTAB_NOHEADER")) || toupper(*ctnh) != 'N') { for (x = 0; x < NHEADER_LINES; x++) { ch = get_char(f); - if (EOF == ch) + if (EOF == ch) { break; + } + if ('#' != ch) { putchar(ch); break; } - while (EOF != (ch = get_char(f))) - if (ch == '\n') + while (EOF != (ch = get_char(f))) { + if (ch == '\n') { break; - if (EOF == ch) + } + } + if (EOF == ch) { break; } } -#endif + } + while (EOF != (ch = get_char(f))) putchar(ch); fclose(f); @@ -365,8 +362,7 @@ exit(ERROR_EXIT); } - if( PromptOnDelete == 1 ) - { + if (PromptOnDelete == 1) { printf("crontab: really delete %s's crontab? (y/n) ", User); fflush(stdout); ans = 0; @@ -382,9 +378,10 @@ fprintf(stderr, "Please enter Y or N: "); } } - if ( (q[0] == 'N') || (q[0] == 'n') ) + if ((q[0] == 'N') || (q[0] == 'n')) { exit(OK_EXIT); } + } log_it(RealUser, Pid, "DELETE", User); if (unlink(n)) { @@ -441,8 +438,7 @@ } /* Now create the actual temporary crontab file */ - if (snprintf(Filename, MAX_FNAME, "%s/crontab", Directory) - >= MAX_FNAME) { + if (snprintf(Filename, MAX_FNAME, "%s/crontab", Directory) >= MAX_FNAME) { fprintf(stderr, "Temporary filename too long - aborting\n"); Filename[0] = '\0'; return -1; @@ -637,21 +633,15 @@ while (EOF != (ch = get_char(f))) putc(ch, NewCrontab); fclose(f); - if (ferror(NewCrontab)) { fprintf(stderr, "%s: error while writing new crontab to %s\n", ProgramName, Filename); } - if (fstat(t, &fsbuf) < 0) { perror("unable to stat temp file"); goto fatal; } - - - /* Okay, edit the file */ - if ((!((editor = getenv("VISUAL")) && strlen(editor))) && (!((editor = getenv("EDITOR")) && strlen(editor))) ) { @@ -674,7 +664,4 @@ (void)signal(SIGQUIT, SIG_IGN); - /* Give up privileges while editing */ - swap_uids(); - switch (pid = fork()) { case -1: @@ -739,9 +726,6 @@ (void)signal(SIGQUIT, SIG_DFL); (void)signal(SIGTSTP, SIG_DFL); - /* Need privs again */ - swap_uids_back(); - switch (open_tmp_crontab(&fsbuf)) { case -1: fprintf(stderr, "Error while editing crontab\n"); @@ -886,7 +870,7 @@ */ 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; @@ -903,6 +887,13 @@ } } + 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); @@ -928,7 +919,6 @@ return (-2); } - if (fclose(tmp) == EOF) { perror("fclose"); unlink(tn); @@ -951,8 +941,6 @@ unlink(tn); return (-2); } - - log_it(RealUser, Pid, "REPLACE", User); poke_daemon(); diff -wu cron-3.0pl1/database.c cron-3.0pl1/database.c --- cron-3.0pl1/database.c +++ cron-3.0pl1/database.c @@ -24,13 +24,12 @@ #include "cron.h" -#define __USE_GNU /* For O_NOFOLLOW */ #include <fcntl.h> -#undef __USE_GNU #include <sys/stat.h> #include <sys/file.h> #include <time.h> + #define TMAX(a,b) ((a)>(b)?(a):(b)) /* Try to get maximum path name -- this isn't really correct, but we're @@ -49,10 +48,8 @@ static void process_crontab __P((char *, char *, char *, struct stat *, cron_db *, cron_db *)); -#ifdef DEBIAN static int valid_name (char *filename); static user *get_next_system_crontab __P((user *)); -#endif void force_rescan_user(cron_db *old_db, cron_db *new_db, const char *fname, time_t old_mtime); @@ -69,13 +66,10 @@ DIR_T *dp; cron_db new_db; user *u, *nu; -#ifdef DEBIAN struct stat syscrond_stat; struct stat syscrond_file_stat; - char syscrond_fname[PATH_MAX+1]; int syscrond_change = 0; -#endif Debug(DLOAD, ("[%d] load_database()\n", getpid())) @@ -95,7 +89,6 @@ syscron_stat.st_mtime = 0; } -#ifdef DEBIAN /* Check mod time of SYSCRONDIR. This won't tell us if a file * in it changed, but will capture deletions, which the individual * file check won't @@ -141,7 +134,6 @@ Debug(DLOAD, (" [checked]\n")) } } -#endif /* DEBIAN */ /* if spooldir's mtime has not changed, we don't need to fiddle with * the database. @@ -150,14 +142,9 @@ * so is guaranteed to be different than the stat() mtime the first * time this function is called. */ -#ifdef DEBIAN if ((old_db->user_mtime == statbuf.st_mtime) && (old_db->sys_mtime == syscron_stat.st_mtime) && (!syscrond_change)) { -#else - if ((old_db->user_mtime == statbuf.st_mtime) && - (old_db->sys_mtime == syscron_stat.st_mtime)) { -#endif Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n", getpid())) return; @@ -170,9 +157,7 @@ */ new_db.user_mtime = statbuf.st_mtime; new_db.sys_mtime = syscron_stat.st_mtime; -#ifdef DEBIAN new_db.sysd_mtime = syscrond_stat.st_mtime; -#endif new_db.head = new_db.tail = NULL; if (syscron_stat.st_mtime) { @@ -181,7 +166,6 @@ &new_db, old_db); } -#ifdef DEBIAN /* Read all the package crontabs. */ if (!(dir = opendir(SYSCRONDIR))) { log_it("CRON", getpid(), "OPENDIR FAILED", SYSCRONDIR); @@ -219,7 +203,6 @@ } if (dir) closedir(dir); -#endif /* we used to keep this dir open all the time, for the sake of * efficiency. however, we need to close it in every fork, and @@ -308,7 +291,7 @@ user * find_user(db, name) cron_db *db; - char *name; + const char *name; { char *env_get(); user *u; @@ -333,13 +316,9 @@ int crontab_fd = OK - 1; user *u = NULL; -#ifdef DEBIAN /* If the name begins with *system*, don't worry about password - it's part of the system crontab */ if (strncmp(fname, "*system*", 8) && !(pw = getpwnam(uname))) { -#else - if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) { -#endif /* file doesn't have a user in passwd file. */ if (strncmp(fname, "tmp.", 4)) { @@ -365,6 +344,7 @@ } /* Check to make sure that the crontab is owned by the correct user (or root) */ + if (statbuf->st_uid != pw->pw_uid && statbuf->st_uid != ROOT_UID) { log_it(fname, getpid(), "WRONG FILE OWNER", tabname); force_rescan_user(old_db, new_db, fname, 0); @@ -404,20 +384,20 @@ } if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) { /* crontab not accessible? - - If tabname is a symlink, it's most probably just broken, so - we force a rescan. Once the link is fixed, it will get picked - up and processed again. If tabname is a regular file, this - error is bad so we skip it instead. + * + * If tabname is a symlink, it's most probably just + * broken, so we force a rescan. Once the link is + * fixed, it will get picked up and processed again. If + * tabname is a regular file, this error is bad, so we + * skip it instead. */ if (S_ISLNK(statbuf->st_mode)) { log_it(fname, getpid(), "CAN'T OPEN SYMLINK", tabname); force_rescan_user(old_db, new_db, fname, 0); - goto next_crontab; } else { log_it(fname, getpid(), "CAN'T OPEN", tabname); - goto next_crontab; } + goto next_crontab; } if (fstat(crontab_fd, statbuf) < OK) { @@ -461,6 +441,7 @@ goto next_crontab; } } + /* * The link count check is not sufficient (the owner may * delete their original link, reducing the link count back to @@ -510,5 +491,4 @@ } - next_crontab: if (crontab_fd >= OK) { @@ -517,7 +497,6 @@ } } -#ifdef DEBIAN #include <regex.h> @@ -568,8 +547,6 @@ return curtab; } -#endif - /* Force rescan of a crontab the next time cron wakes up * * cron currently only detects changes caused by an mtime update; it does not @@ -595,10 +572,12 @@ /* Allocate an empty crontab with the specified mtime, add it to new DB */ if ((u = (user *) malloc(sizeof(user))) == NULL) { errno = ENOMEM; + return; } if ((u->name = strdup(fname)) == NULL) { free(u); errno = ENOMEM; + return; } u->mtime = old_mtime; u->crontab = NULL; diff -wu cron-3.0pl1/debian/changelog cron-3.0pl1/debian/changelog --- cron-3.0pl1/debian/changelog +++ cron-3.0pl1/debian/changelog @@ -1,3 +1,32 @@ +cron (3.0pl1-133) unstable; urgency=medium + + * SECURITY: Fix bypass of /etc/cron.{allow,deny} on failure to open + If these files exist, then they must be readable by the user executing + crontab(1). Users will now be denied by default if they aren't. + (LP: #1813833) + * SECURITY: Fix for possible DoS by use-after-free + A user reported a use-after-free condition in the cron daemon, leading to a + possible Denial-of-Service scenario by crashing the daemon. + (Closes: #809167) + * SECURITY: DoS: Fix unchecked return of calloc() + Florian Weimer discovered that a missing check for the return value of + calloc() could crash the daemon, which could be triggered by a very + large crontab created by a user. + * Enforce maximum crontab line count of 1000 to prevent a malicious user + from creating an excessivly large crontab. The daemon will log a warning + for existing files, and crontab(1) will refuse to create new ones. + * Add d/NEWS altering to the new 1000 lines limit. + * Move /var/run/crond.reboot to /run/crond.reboot. + * crontab.5: Reverse the info on tilde expansion. When setting PATH, most + shells will not expand a tilde. Thanks, Tim Landscheidt, for the analysis. + (Closes: #801328) + * Fixes for numerous man page issues. Remove trailing whitespace, use proper + escapes, etc. Thanks, Bjarni Ingi Gislason! (Closes: #893575, #893579) + * crontab.1: Drop duplicate DIAGNOSTICS header. + * daemon: Only support the 'x' debug option in debug builds. + + -- Christian Kastner <c...@debian.org> Sun, 10 Mar 2019 17:49:18 +0100 + cron (3.0pl1-132) unstable; urgency=medium [ Christian Kastner ] diff -wu cron-3.0pl1/do_command.c cron-3.0pl1/do_command.c --- cron-3.0pl1/do_command.c +++ cron-3.0pl1/do_command.c @@ -132,7 +132,6 @@ char *usernm, *mailto; int children = 0; pid_t job_pid; - #if defined(USE_PAM) int retcode = 0; #endif @@ -173,13 +172,9 @@ /* our parent is watching for our death by catching SIGCHLD. we * do not care to watch for our children's deaths this way -- we - * use wait() explictly. so we have to disable the signal (which + * use wait() explictly. so we have to reset the signal (which * was inherited from the parent). */ -#ifdef DEBIAN (void) signal(SIGCHLD, SIG_DFL); #else - (void) signal(SIGCHLD, SIG_IGN); -#endif -#else /* on system-V systems, we are ignoring SIGCLD. we have to stop * ignoring it now or the wait() in cron_pclose() won't work. @@ -245,7 +240,6 @@ PAM_FAIL_CHECK; retcode = pam_open_session(pamh, PAM_SILENT); PAM_FAIL_CHECK; - #endif /* fork again, this time so we can exec the user's command. @@ -288,11 +282,9 @@ /* grandchild process. make std{in,out} be the ends of * pipes opened by our daddy; make stderr go to stdout. */ - /* Closes are unnecessary -- let dup2() do it */ - - /* close(STDIN) */; dup2(stdin_pipe[READ_PIPE], STDIN); + dup2(stdin_pipe[READ_PIPE], STDIN); dup2(fileno(tmpout), STDOUT); - /* close(STDERR)*/; dup2(STDOUT, STDERR); + dup2(STDOUT, STDERR); /* close the pipe we just dup'ed. The resources will remain. @@ -348,26 +340,20 @@ _exit(OK_EXIT); } # endif /*DEBUGGING*/ -#if 0 - { - struct sigaction oact; - sigaction(SIGCHLD, NULL, &oact); - } - fprintf(stdout,"error"); -#endif #ifdef WITH_SELINUX if (is_selinux_enabled() > 0) { if (u->scontext != 0L) { - if (setexeccon(u->scontext) < 0) { - if (security_getenforce() > 0) { - fprintf(stderr, "Could not set exec context to %s for user %s\n", u->scontext,u->name); + if (setexeccon(u->scontext) < 0 && + security_getenforce() > 0) { + fprintf(stderr, "Could not set exec context " + "to %s for user %s\n", + u->scontext,u->name); _exit(ERROR_EXIT); } - } - } - else if(security_getenforce() > 0) - { - fprintf(stderr, "Error, must have a security context for the cron job when in enforcing mode.\nUser %s.\n", u->name); + } else if (security_getenforce() > 0) { + fprintf(stderr, "Error, must have a security context " + "for the cron job when in enforcing " + "mode.\nUser %s.\n", u->name); _exit(ERROR_EXIT); } } @@ -513,7 +499,6 @@ long pos; struct stat mcsb; - int statret; fseek(tmpout, 0, SEEK_END); pos = ftell(tmpout); @@ -531,7 +516,7 @@ goto mail_finished; /* Don't send mail if MAILCMD is not available */ - if ((statret = stat(MAILCMD, &mcsb)) != 0) { + if (stat(MAILCMD, &mcsb) != 0) { Debug(DPROC|DEXT, ("%s not found, not sending mail\n", MAILCMD)) if (pos > 0) { log_it("CRON", getpid(), "info", "No MTA installed, discarding output"); @@ -569,10 +554,10 @@ arpadate(&StartTime)); # endif /* MAIL_DATE */ fprintf(mail, "MIME-Version: 1.0\n"); + if ( content_type == 0L ) { fprintf(mail, "Content-Type: text/plain; charset=%s\n", - cron_default_mail_charset - ); + cron_default_mail_charset); } else { /* user specified Content-Type header. * disallow new-lines for security reasons @@ -581,24 +566,28 @@ char *nl=content_type; size_t ctlen = strlen(content_type); - while( (*nl != '\0') - && ((nl=strchr(nl,'\n')) != 0L) - && (nl < (content_type+ctlen)) - ) *nl = ' '; + while ((*nl != '\0') && + ((nl=strchr(nl,'\n')) != 0L) && + (nl < (content_type+ctlen))) { + *nl = ' '; + } fprintf(mail,"Content-Type: %s\n", content_type); } + if ( content_transfer_encoding != 0L ) { char *nl=content_transfer_encoding; size_t ctlen = strlen(content_transfer_encoding); - while( (*nl != '\0') - && ((nl=strchr(nl,'\n')) != 0L) - && (nl < (content_transfer_encoding+ctlen)) - ) *nl = ' '; - - fprintf(mail,"Content-Transfer-Encoding: %s\n", content_transfer_encoding); + while ((*nl != '\0') && + ((nl = strchr(nl,'\n')) != 0L) && + (nl < (content_transfer_encoding+ctlen))) { + *nl = ' '; } - else + + fprintf(mail,"Content-Transfer-Encoding: %s\n", + content_transfer_encoding); + } else { fprintf(mail, "Content-Transfer-Encoding: 8bit\n"); + } for (env = e->envp; *env; env++) fprintf(mail, "X-Cron-Env: <%s>\n", diff -wu cron-3.0pl1/entry.c cron-3.0pl1/entry.c --- cron-3.0pl1/entry.c +++ cron-3.0pl1/entry.c @@ -108,6 +108,10 @@ */ e = (entry *) calloc(sizeof(entry), sizeof(char)); + if (e == NULL) { + log_it("CRON", getpid(), "OOM", "Out of memory parsing crontab"); + return NULL; + } if (ch == '@') { /* all of these should be flagged and load-limited; i.e., diff -wu cron-3.0pl1/env.c cron-3.0pl1/env.c --- cron-3.0pl1/env.c +++ cron-3.0pl1/env.c @@ -278,7 +278,7 @@ register int len = strlen(name); register char *p, *q; - while ((p = *envp++)) { + while ((p = *envp++) != NULL) { if (!(q = strchr(p, '='))) continue; if ((q - p) == len && !strncmp(p, name, len)) diff -wu cron-3.0pl1/externs.h cron-3.0pl1/externs.h --- cron-3.0pl1/externs.h +++ cron-3.0pl1/externs.h @@ -61,7 +61,6 @@ extern void perror(), exit(), free(); extern char *getenv(), *strcpy(), *strchr(), *strtok(); extern void *malloc(), *realloc(); - # define SIG_T void # define TIME_T long # define PID_T int diff -wu cron-3.0pl1/misc.c cron-3.0pl1/misc.c --- cron-3.0pl1/misc.c +++ cron-3.0pl1/misc.c @@ -213,8 +213,7 @@ fprintf(stderr, "%s: created\n", CRONDIR); stat(CRONDIR, &sb); } else { - fprintf(stderr, "%s: mkdir: %s\n", CRONDIR, - strerror(errno)); + fprintf(stderr, "%s: mkdir: %s\n", CRONDIR, strerror(errno)); exit(ERROR_EXIT); } (void) umask(um); @@ -331,7 +330,6 @@ /* abandon fd and fp even though the file is open. we need to * keep it open and locked, but we don't need the handles elsewhere. */ - } /* get_char(file) : like getc() but increment LineNumber on newlines @@ -344,7 +342,7 @@ ch = getc(file); if (ch == '\n') - Set_LineNum(LineNumber + 1); + Set_LineNum(LineNumber + 1) return ch; } @@ -358,7 +356,7 @@ { ungetc(ch, file); if (ch == '\n') - Set_LineNum(LineNumber - 1); + Set_LineNum(LineNumber - 1) } @@ -479,7 +477,23 @@ init = TRUE; #if defined(ALLOW_FILE) && defined(DENY_FILE) allow = fopen(ALLOW_FILE, "r"); + if (allow == NULL) { + /* Only if the file does not exist do we ignore the + * error. Otherwise, we deny by default. + */ + if (errno != ENOENT) { + perror(ALLOW_FILE); + return FALSE; + } + } deny = fopen(DENY_FILE, "r"); + if (allow == NULL) { + /* See above */ + if (errno != ENOENT) { + perror(DENY_FILE); + return FALSE; + } + } Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny)) #else allow = NULL; @@ -515,15 +529,14 @@ char *event; char *detail; { -#if defined(LOG_FILE) PID_T pid = xpid; +#if defined(LOG_FILE) char *msg; TIME_T now = time((TIME_T) 0); register struct tm *t = localtime(&now); int msg_size; #endif /*LOG_FILE*/ - #if defined(LOG_FILE) /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. */ @@ -567,15 +580,6 @@ #endif /*LOG_FILE*/ #if defined(SYSLOG) - - - /* we don't use LOG_PID since the pid passed to us by - * our client may not be our own. therefore we want to - * print the pid ourselves. - */ - /* SteveG says: That comment is not consistent with the - code, and makes no sense -- I suspect it's a remnant - of a cut-n-paster... */ # ifdef LOG_CRON openlog(ProgramName, LOG_PID, LOG_CRON); # else @@ -584,7 +588,6 @@ syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail); - closelog(); #endif /*SYSLOG*/ #if DEBUGGING diff -wu cron-3.0pl1/pathnames.h cron-3.0pl1/pathnames.h --- cron-3.0pl1/pathnames.h +++ cron-3.0pl1/pathnames.h @@ -19,7 +19,7 @@ * $Id: pathnames.h,v 1.3 1994/01/15 20:43:43 vixie Exp $ */ -#if (defined(BSD)) && (BSD >= 199103) || defined(__linux__) || defined(AIX) || defined(__GNU__) || defined(__GLIBC__) +#if (defined(BSD)) && (BSD >= 199103) || defined(__linux__) || defined(AIX) || defined(__GLIBC__) # include <paths.h> #endif /*BSD*/ @@ -47,14 +47,9 @@ * LOG_FILE or SYSLOG is defined, we don't log. If * both are defined, we log both ways. */ -#ifdef DEBIAN #define ALLOW_FILE "/etc/cron.allow" /*-*/ #define DENY_FILE "/etc/cron.deny" /*-*/ -#else -#define ALLOW_FILE "allow" /*-*/ -#define DENY_FILE "deny" /*-*/ -#endif -/* #define LOG_FILE "log" -*/ +/* #define LOG_FILE "log" /*-*/ /* where should the daemon stick its PID? */ @@ -67,20 +62,13 @@ /* 4.3BSD-style crontab */ #define SYSCRONTAB "/etc/crontab" -#ifdef DEBIAN /* where package specific crontabs live */ #define SYSCRONDIR "/etc/cron.d" -#endif + /* what editor to use if no EDITOR or VISUAL * environment variable specified. */ -#if defined(DEBIAN) # define EDITOR "/usr/bin/sensible-editor" -#elif defined(_PATH_VI) -# define EDITOR _PATH_VI -#else -# define EDITOR "/usr/ucb/vi" -#endif #ifndef _PATH_BSHELL # define _PATH_BSHELL "/bin/sh" @@ -90,12 +78,11 @@ # define _PATH_DEFPATH "/usr/bin:/bin" #endif -#ifndef _PATH_DEFPATH_ROOT -# define _PATH_DEFPATH_ROOT "/usr/sbin:/usr/bin:/sbin:/bin" +#ifndef REBOOT_FILE +# define REBOOT_FILE "/run/crond.reboot" #endif -#ifdef DEBIAN #ifndef CRONDIR_MODE /* Create mode for CRONDIR; must be in sync with * packaging @@ -117 +103,0 @@ -#endif diff -wu cron-3.0pl1/popen.c cron-3.0pl1/popen.c --- cron-3.0pl1/popen.c +++ cron-3.0pl1/popen.c @@ -148,7 +148,6 @@ exit(ERROR_EXIT); } chdir(env_get("HOME", e->envp)); - #if WANT_GLOBBING execvp(gargv[0], gargv); #else diff -wu cron-3.0pl1/user.c cron-3.0pl1/user.c --- cron-3.0pl1/user.c +++ cron-3.0pl1/user.c @@ -58,9 +58,7 @@ freecon(current_con); return (security_getenforce() > 0); } - } - else - { + } else { context_t temp_con = context_new(current_con); if (temp_con == NULL) { log_it(name, getpid(), "context_new FAILED", tabname); @@ -110,8 +108,7 @@ * permission check for this purpose. */ - for(i = 0; i < list_count; i++) - { + for (i = 0; i < list_count; i++) { retval = security_compute_av(context_list[i], file_context, SECCLASS_FILE, @@ -139,7 +136,6 @@ #endif -#ifdef DEBIAN /* Function used to log errors in crontabs from cron daemon. (User crontabs are checked before they're accepted, but system crontabs are not. */ @@ -163,8 +159,6 @@ } } -#endif - void free_user(u) user *u; @@ -236,7 +230,6 @@ } #endif - /* * init environment. this will be copied/augmented for each entry. */ @@ -249,6 +242,7 @@ /* * load the crontab */ + Set_LineNum(1) do { status = load_env(envstr, file); switch (status) { @@ -268,13 +262,9 @@ } goto done; case FALSE: -#ifdef DEBIAN err_user = fname; e = load_entry(file, crontab_error, pw, envp); err_user = NULL; -#else - e = load_entry(file, NULL, pw, envp); -#endif if (e) { e->next = u->crontab; u->crontab = e; @@ -298,7 +288,19 @@ } 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); only in patch2: --- cron-3.0pl1.orig/debian/NEWS +++ cron-3.0pl1/debian/NEWS @@ -0,0 +1,9 @@ +cron (3.0pl1-133) unstable; urgency=medium + + * As a reasonable protective measure, crontabs are now limited to 1000 lines + in length per crontab. + The maintainers find it very unlikely that longer crontabs exist; however, + if you do have a use case, please file a bug report with a brief rationale, + and we will consider raising this limit. + + -- Christian Kastner <c...@debian.org> Sun, 10 Mar 2019 17:44:13 +0100
diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/debian/changelog cron-3.0pl1/debian/changelog --- cron-3.0pl1/debian/changelog 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/debian/changelog 2019-03-21 20:14:33.000000000 +0100 @@ -1,3 +1,32 @@ +cron (3.0pl1-133) unstable; urgency=medium + + * SECURITY: Fix bypass of /etc/cron.{allow,deny} on failure to open + If these files exist, then they must be readable by the user executing + crontab(1). Users will now be denied by default if they aren't. + (LP: #1813833) + * SECURITY: Fix for possible DoS by use-after-free + A user reported a use-after-free condition in the cron daemon, leading to a + possible Denial-of-Service scenario by crashing the daemon. + (Closes: #809167) + * SECURITY: DoS: Fix unchecked return of calloc() + Florian Weimer discovered that a missing check for the return value of + calloc() could crash the daemon, which could be triggered by a very + large crontab created by a user. + * Enforce maximum crontab line count of 1000 to prevent a malicious user + from creating an excessivly large crontab. The daemon will log a warning + for existing files, and crontab(1) will refuse to create new ones. + * Add d/NEWS altering to the new 1000 lines limit. + * Move /var/run/crond.reboot to /run/crond.reboot. + * crontab.5: Reverse the info on tilde expansion. When setting PATH, most + shells will not expand a tilde. Thanks, Tim Landscheidt, for the analysis. + (Closes: #801328) + * Fixes for numerous man page issues. Remove trailing whitespace, use proper + escapes, etc. Thanks, Bjarni Ingi Gislason! (Closes: #893575, #893579) + * crontab.1: Drop duplicate DIAGNOSTICS header. + * daemon: Only support the 'x' debug option in debug builds. + + -- Christian Kastner <c...@debian.org> Sun, 10 Mar 2019 17:49:18 +0100 + cron (3.0pl1-132) unstable; urgency=medium [ Christian Kastner ] diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/compat.h cron-3.0pl1/compat.h --- cron-3.0pl1/compat.h 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/compat.h 2019-03-21 20:14:33.000000000 +0100 @@ -62,8 +62,8 @@ #endif #ifndef POSIX -# if (BSD >= 199103) || defined(__linux__) || defined(__GNU__) || defined(ultrix) ||\ - defined(AIX) ||\ defined(HPUX) || defined(CONVEX) || defined(IRIX) || defined(__GLIBC__) +# if (BSD >= 199103) || defined(__linux__) || defined(ultrix) || defined(AIX) ||\ + defined(HPUX) || defined(CONVEX) || defined(IRIX) # define POSIX # endif #endif @@ -76,17 +76,17 @@ /*****************************************************************/ -#if !defined(BSD) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux__) && !defined(__GNU__) +#if !defined(BSD) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux__) && !defined(__GLIBC__) # define NEED_VFORK #endif #if (!defined(BSD) || (BSD < 198902)) && !defined(__linux__) && \ - !defined(IRIX) && !defined(NeXT) && !defined(HPUX) && !defined(__GNU__) && !defined(__GLIBC__) + !defined(IRIX) && !defined(NeXT) && !defined(HPUX) && !defined(__GLIBC__) # define NEED_STRCASECMP #endif #if (!defined(BSD) || (BSD < 198911)) && !defined(__linux__) &&\ - !defined(IRIX) && !defined(UNICOS) && !defined(HPUX) && !defined(__GNU__) && !defined(__GLIBC__) + !defined(IRIX) && !defined(UNICOS) && !defined(HPUX) && !defined(__GLIBC__) # define NEED_STRDUP #endif @@ -102,7 +102,7 @@ # define NEED_SETSID #endif -#if (defined(POSIX) && !defined(BSD)) && !defined(__linux__) && !defined(__GNU__) && !defined(__GLIBC__) +#if (defined(POSIX) && !defined(BSD)) && !defined(__linux__) && !defined(__GLIBC__) # define NEED_GETDTABLESIZE #endif @@ -110,11 +110,11 @@ # define HAVE_SAVED_UIDS #endif -#if !defined(ATT) && !defined(__linux__) && !defined(__GNU__) && !defined(IRIX) && !defined(UNICOS) && !defined(__GLIBC__) +#if (!defined(ATT) && !defined(IRIX) && !defined(UNICOS)) || defined(POSIX) # define USE_SIGCHLD #endif -#if !defined(AIX) && !defined(UNICOS) && !defined(DEBIAN) +#if !defined(AIX) && !defined(UNICOS) && !defined(POSIX) # define SYS_TIME_H 1 #else # define SYS_TIME_H 0 diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/config.h cron-3.0pl1/config.h --- cron-3.0pl1/config.h 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/config.h 2019-03-21 20:14:33.000000000 +0100 @@ -43,10 +43,10 @@ */ #define MAILCMD _PATH_SENDMAIL /*-*/ -/* #define MAILARGS "%s -i -FCronDaemon -odi -oem %s" /*-*/ -#define MAILARGS "%s -i -FCronDaemon -B8BITMIME -oem %s" /*-*/ - /* -i = don't terminate on "." by itself - * -Fx = set full-name of sender +#define MAILARGS "%s -FCronDaemon -i -B8BITMIME -oem %s" /*-*/ + /* -Fx = set full-name of sender + * -i = don't terminate on "." by itself + * -B8BITMIME = 8-bit processing * -odi = Option Deliverymode Interactive * -oem = Option Errors Mailedtosender * -t = read recipient from header of message @@ -55,15 +55,15 @@ * by joe user. --okir */ -/* #define MAILCMD "/bin/mail" -*/ -/* #define MAILARGS "%s -d %s" -*/ +/* #define MAILCMD "/bin/mail" /*-*/ +/* #define MAILARGS "%s -d %s" /*-*/ /* -d = undocumented but common flag: deliver locally? */ -/* #define MAILCMD "/usr/mmdf/bin/submit" -*/ -/* #define MAILARGS "%s -mlrxto %s" -*/ +/* #define MAILCMD "/usr/mmdf/bin/submit" /*-*/ +/* #define MAILARGS "%s -mlrxto %s" /*-*/ -/* #define MAIL_DATE -*/ +/* #define MAIL_DATE /*-*/ /* should we include an ersatz Date: header in * generated mail? if you are using sendmail * for MAILCMD, it is better to let sendmail @@ -74,7 +74,7 @@ * defined but neither exists, should crontab(1) be * usable only by root? */ -/*#define ALLOW_ONLY_ROOT -*/ +/*#define ALLOW_ONLY_ROOT /*-*/ /* if you want to use syslog(3) instead of appending * to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/cron.c cron-3.0pl1/cron.c --- cron-3.0pl1/cron.c 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/cron.c 2019-03-21 20:14:33.000000000 +0100 @@ -26,6 +26,7 @@ #include "cron.h" #include <signal.h> +#include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <libgen.h> @@ -49,8 +50,9 @@ char **dflags; fprintf(stderr, "usage: %s [-x [", ProgramName); - for(dflags = DebugFlagNames; *dflags; dflags++) + for (dflags = DebugFlagNames; *dflags; dflags++) { fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "]"); + } fprintf(stderr, "]\n"); #else fprintf(stderr, "usage: %s\n", ProgramName); @@ -83,9 +85,6 @@ #endif (void) signal(SIGHUP, sighup_handler); - /* Reopen stdin in case some idiot closed it before starting - us - it will only be closed, but not having it open here - screws up other things that will be opened */ if (fdopen(0,"r") == NULL) { (void) open("dev/null", 0); } @@ -145,11 +144,8 @@ database.tail = NULL; database.sys_mtime = (time_t) 0; database.user_mtime = (time_t) 0; -#ifdef DEBIAN database.sysd_mtime = (time_t) 0; -#endif load_database(&database); - set_time(TRUE); run_reboot_jobs(&database); timeRunning = virtualTime = clockTime; @@ -273,10 +269,6 @@ } } -#ifdef DEBIAN -#include <sys/stat.h> -#include <fcntl.h> -#endif static void run_reboot_jobs(db) @@ -285,8 +277,7 @@ register user *u; register entry *e; int rbfd; -#ifdef DEBIAN -#define REBOOT_FILE "/var/run/crond.reboot" + /* Run on actual reboot, rather than cron restart */ if (access(REBOOT_FILE, F_OK) == 0) { /* File exists, return */ @@ -303,12 +294,7 @@ close(rbfd); log_it("CRON", getpid(),"INFO", "Running @reboot jobs"); } - - - Debug(DMISC, ("[%d], Debian running reboot jobs\n",getpid())); - -#endif - Debug(DMISC, ("[%d], vixie running reboot jobs\n", getpid())); + Debug(DMISC, ("[%d], running reboot jobs\n", getpid())); for (u = db->head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { if (e->flags & WHEN_REBOOT) { @@ -418,7 +404,7 @@ #ifdef USE_SIGCHLD static void -sigchld_handler(x) { +sigchld_handler(int x) { int save_errno = errno; WAIT_T waiter; PID_T pid; @@ -452,7 +438,7 @@ static void -sighup_handler(x) { +sighup_handler(int x) { log_close(); /* we should use sigaction for proper signal blocking as this @@ -461,6 +447,12 @@ } +#if DEBUGGING +const char *getoptarg = "flL:nx:"; +#else +const char *getoptarg = "flL:n"; +#endif + static void parse_args(argc, argv) int argc; @@ -468,31 +460,33 @@ { int argch; - log_level = 1; stay_foreground = 0; lsbsysinit_mode = 0; + log_level = 1; fqdn_in_subject = 0; - while (EOF != (argch = getopt(argc, argv, "lfnx:L:"))) { + while (EOF != (argch = getopt(argc, argv, getoptarg))) { switch (argch) { default: usage(); case 'f': stay_foreground = 1; break; - case 'x': - if (!set_debug_flags(optarg)) - usage(); - break; case 'l': lsbsysinit_mode = 1; break; + case 'L': + log_level = atoi(optarg); + break; case 'n': fqdn_in_subject = 1; break; - case 'L': - log_level = atoi(optarg); +#if DEBUGGING + case 'x': + if (!set_debug_flags(optarg)) + usage(); break; +#endif } } } diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/cron.h cron-3.0pl1/cron.h --- cron-3.0pl1/cron.h 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/cron.h 2019-03-21 20:14:33.000000000 +0100 @@ -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)); \ @@ -209,9 +213,7 @@ user *head, *tail; /* links */ time_t user_mtime; /* last modtime on spooldir */ time_t sys_mtime; /* last modtime on system crontab */ -#ifdef DEBIAN time_t sysd_mtime; /* last modtime on system crondir */ -#endif } cron_db; typedef struct _orphan { @@ -263,7 +265,7 @@ **env_set __P((char **, char *)); user *load_user __P((int, struct passwd *, char *, char *, char *)), - *find_user __P((cron_db *, char *)); + *find_user __P((cron_db *, const char *)); entry *load_entry __P((FILE *, void (*)(), struct passwd *, char **)); @@ -305,8 +307,9 @@ int stay_foreground; int lsbsysinit_mode; +int log_level; int fqdn_in_subject; -int log_level = 1; + char cron_default_mail_charset[MAX_ENVSTR] = ""; # if DEBUGGING @@ -322,8 +325,8 @@ *DowNames[], *ProgramName; extern int lsbsysinit_mode; -extern int fqdn_in_subject; extern int log_level; +extern int fqdn_in_subject; extern int LineNumber; extern time_t StartTime; extern time_min timeRunning; diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/crontab.c cron-3.0pl1/crontab.c --- cron-3.0pl1/crontab.c 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/crontab.c 2019-03-21 20:14:33.000000000 +0100 @@ -46,8 +46,6 @@ #endif -#define NHEADER_LINES 3 - enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; #if DEBUGGING @@ -154,12 +152,11 @@ } #if DEBUGGING -char *getoptarg = "u:lerix:"; +const char *getoptarg = "u:lerix:"; #else -char *getoptarg = "u:leri"; +const char *getoptarg = "u:leri"; #endif - static void parse_args(argc, argv) int argc; @@ -189,7 +186,6 @@ case 'x': if (!set_debug_flags(optarg)) usage("bad debug option"); - usage("unrecognized option"); break; #endif case 'u': @@ -302,10 +298,8 @@ char n[MAX_FNAME]; FILE *f; int ch; -#ifdef DEBIAN int x; char *ctnh; -#endif log_it(RealUser, Pid, "LIST", User); (void) snprintf(n, MAX_FNAME, CRON_TAB(User)); @@ -321,30 +315,33 @@ /* file is open. copy to stdout, close. */ Set_LineNum(1) -#ifdef DEBIAN - /* DEBIAN: Don't list header lines unless CRONTAB_NOHEADER is - 'N'. */ - /* ignore the top few comments since we probably put them there. + + /* Don't list header lines unless CRONTAB_NOHEADER is 'N'. + * + * ignore the top few comments since we probably put them there. */ - if (!(ctnh = getenv("CRONTAB_NOHEADER")) || - toupper(*ctnh) != 'N') - { + if (!(ctnh = getenv("CRONTAB_NOHEADER")) || toupper(*ctnh) != 'N') { for (x = 0; x < NHEADER_LINES; x++) { ch = get_char(f); - if (EOF == ch) + if (EOF == ch) { break; + } + if ('#' != ch) { putchar(ch); break; } - while (EOF != (ch = get_char(f))) - if (ch == '\n') + while (EOF != (ch = get_char(f))) { + if (ch == '\n') { break; - if (EOF == ch) + } + } + if (EOF == ch) { break; } } -#endif + } + while (EOF != (ch = get_char(f))) putchar(ch); fclose(f); @@ -365,8 +362,7 @@ exit(ERROR_EXIT); } - if( PromptOnDelete == 1 ) - { + if (PromptOnDelete == 1) { printf("crontab: really delete %s's crontab? (y/n) ", User); fflush(stdout); ans = 0; @@ -382,9 +378,10 @@ fprintf(stderr, "Please enter Y or N: "); } } - if ( (q[0] == 'N') || (q[0] == 'n') ) + if ((q[0] == 'N') || (q[0] == 'n')) { exit(OK_EXIT); } + } log_it(RealUser, Pid, "DELETE", User); if (unlink(n)) { @@ -441,8 +438,7 @@ } /* Now create the actual temporary crontab file */ - if (snprintf(Filename, MAX_FNAME, "%s/crontab", Directory) - >= MAX_FNAME) { + if (snprintf(Filename, MAX_FNAME, "%s/crontab", Directory) >= MAX_FNAME) { fprintf(stderr, "Temporary filename too long - aborting\n"); Filename[0] = '\0'; return -1; @@ -637,21 +633,15 @@ while (EOF != (ch = get_char(f))) putc(ch, NewCrontab); fclose(f); - if (ferror(NewCrontab)) { fprintf(stderr, "%s: error while writing new crontab to %s\n", ProgramName, Filename); } - if (fstat(t, &fsbuf) < 0) { perror("unable to stat temp file"); goto fatal; } - - - /* Okay, edit the file */ - if ((!((editor = getenv("VISUAL")) && strlen(editor))) && (!((editor = getenv("EDITOR")) && strlen(editor))) ) { @@ -673,9 +663,6 @@ (void)signal(SIGINT, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); - /* Give up privileges while editing */ - swap_uids(); - switch (pid = fork()) { case -1: perror("fork"); @@ -739,9 +726,6 @@ (void)signal(SIGQUIT, SIG_DFL); (void)signal(SIGTSTP, SIG_DFL); - /* Need privs again */ - swap_uids_back(); - switch (open_tmp_crontab(&fsbuf)) { case -1: fprintf(stderr, "Error while editing crontab\n"); @@ -886,7 +870,7 @@ */ 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; @@ -903,6 +887,13 @@ } } + 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); @@ -928,7 +919,6 @@ return (-2); } - if (fclose(tmp) == EOF) { perror("fclose"); unlink(tn); @@ -951,8 +941,6 @@ unlink(tn); return (-2); } - - log_it(RealUser, Pid, "REPLACE", User); poke_daemon(); diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/database.c cron-3.0pl1/database.c --- cron-3.0pl1/database.c 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/database.c 2019-03-21 20:14:33.000000000 +0100 @@ -24,13 +24,12 @@ #include "cron.h" -#define __USE_GNU /* For O_NOFOLLOW */ #include <fcntl.h> -#undef __USE_GNU #include <sys/stat.h> #include <sys/file.h> #include <time.h> + #define TMAX(a,b) ((a)>(b)?(a):(b)) /* Try to get maximum path name -- this isn't really correct, but we're @@ -49,10 +48,8 @@ static void process_crontab __P((char *, char *, char *, struct stat *, cron_db *, cron_db *)); -#ifdef DEBIAN static int valid_name (char *filename); static user *get_next_system_crontab __P((user *)); -#endif void force_rescan_user(cron_db *old_db, cron_db *new_db, const char *fname, time_t old_mtime); @@ -69,13 +66,10 @@ DIR_T *dp; cron_db new_db; user *u, *nu; -#ifdef DEBIAN struct stat syscrond_stat; struct stat syscrond_file_stat; - char syscrond_fname[PATH_MAX+1]; int syscrond_change = 0; -#endif Debug(DLOAD, ("[%d] load_database()\n", getpid())) @@ -95,7 +89,6 @@ syscron_stat.st_mtime = 0; } -#ifdef DEBIAN /* Check mod time of SYSCRONDIR. This won't tell us if a file * in it changed, but will capture deletions, which the individual * file check won't @@ -141,7 +134,6 @@ Debug(DLOAD, (" [checked]\n")) } } -#endif /* DEBIAN */ /* if spooldir's mtime has not changed, we don't need to fiddle with * the database. @@ -150,14 +142,9 @@ * so is guaranteed to be different than the stat() mtime the first * time this function is called. */ -#ifdef DEBIAN if ((old_db->user_mtime == statbuf.st_mtime) && (old_db->sys_mtime == syscron_stat.st_mtime) && (!syscrond_change)) { -#else - if ((old_db->user_mtime == statbuf.st_mtime) && - (old_db->sys_mtime == syscron_stat.st_mtime)) { -#endif Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n", getpid())) return; @@ -170,9 +157,7 @@ */ new_db.user_mtime = statbuf.st_mtime; new_db.sys_mtime = syscron_stat.st_mtime; -#ifdef DEBIAN new_db.sysd_mtime = syscrond_stat.st_mtime; -#endif new_db.head = new_db.tail = NULL; if (syscron_stat.st_mtime) { @@ -181,7 +166,6 @@ &new_db, old_db); } -#ifdef DEBIAN /* Read all the package crontabs. */ if (!(dir = opendir(SYSCRONDIR))) { log_it("CRON", getpid(), "OPENDIR FAILED", SYSCRONDIR); @@ -219,7 +203,6 @@ } if (dir) closedir(dir); -#endif /* we used to keep this dir open all the time, for the sake of * efficiency. however, we need to close it in every fork, and @@ -308,7 +291,7 @@ user * find_user(db, name) cron_db *db; - char *name; + const char *name; { char *env_get(); user *u; @@ -333,13 +316,9 @@ int crontab_fd = OK - 1; user *u = NULL; -#ifdef DEBIAN /* If the name begins with *system*, don't worry about password - it's part of the system crontab */ if (strncmp(fname, "*system*", 8) && !(pw = getpwnam(uname))) { -#else - if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) { -#endif /* file doesn't have a user in passwd file. */ if (strncmp(fname, "tmp.", 4)) { @@ -365,6 +344,7 @@ } /* Check to make sure that the crontab is owned by the correct user (or root) */ + if (statbuf->st_uid != pw->pw_uid && statbuf->st_uid != ROOT_UID) { log_it(fname, getpid(), "WRONG FILE OWNER", tabname); force_rescan_user(old_db, new_db, fname, 0); @@ -404,20 +384,20 @@ } if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) { /* crontab not accessible? - - If tabname is a symlink, it's most probably just broken, so - we force a rescan. Once the link is fixed, it will get picked - up and processed again. If tabname is a regular file, this - error is bad so we skip it instead. + * + * If tabname is a symlink, it's most probably just + * broken, so we force a rescan. Once the link is + * fixed, it will get picked up and processed again. If + * tabname is a regular file, this error is bad, so we + * skip it instead. */ if (S_ISLNK(statbuf->st_mode)) { log_it(fname, getpid(), "CAN'T OPEN SYMLINK", tabname); force_rescan_user(old_db, new_db, fname, 0); - goto next_crontab; } else { log_it(fname, getpid(), "CAN'T OPEN", tabname); - goto next_crontab; } + goto next_crontab; } if (fstat(crontab_fd, statbuf) < OK) { @@ -461,6 +441,7 @@ goto next_crontab; } } + /* * The link count check is not sufficient (the owner may * delete their original link, reducing the link count back to @@ -509,7 +490,6 @@ force_rescan_user(old_db, new_db, fname, statbuf->st_mtime); } - next_crontab: if (crontab_fd >= OK) { Debug(DLOAD, (" [done]\n")) @@ -517,7 +497,6 @@ } } -#ifdef DEBIAN #include <regex.h> @@ -568,8 +547,6 @@ return curtab; } -#endif - /* Force rescan of a crontab the next time cron wakes up * * cron currently only detects changes caused by an mtime update; it does not @@ -595,10 +572,12 @@ /* Allocate an empty crontab with the specified mtime, add it to new DB */ if ((u = (user *) malloc(sizeof(user))) == NULL) { errno = ENOMEM; + return; } if ((u->name = strdup(fname)) == NULL) { free(u); errno = ENOMEM; + return; } u->mtime = old_mtime; u->crontab = NULL; diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/debian/NEWS cron-3.0pl1/debian/NEWS --- cron-3.0pl1/debian/NEWS 1970-01-01 01:00:00.000000000 +0100 +++ cron-3.0pl1/debian/NEWS 2019-03-21 20:14:33.000000000 +0100 @@ -0,0 +1,9 @@ +cron (3.0pl1-133) unstable; urgency=medium + + * As a reasonable protective measure, crontabs are now limited to 1000 lines + in length per crontab. + The maintainers find it very unlikely that longer crontabs exist; however, + if you do have a use case, please file a bug report with a brief rationale, + and we will consider raising this limit. + + -- Christian Kastner <c...@debian.org> Sun, 10 Mar 2019 17:44:13 +0100 diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/do_command.c cron-3.0pl1/do_command.c --- cron-3.0pl1/do_command.c 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/do_command.c 2019-03-21 20:14:33.000000000 +0100 @@ -132,7 +132,6 @@ char *usernm, *mailto; int children = 0; pid_t job_pid; - #if defined(USE_PAM) int retcode = 0; #endif @@ -172,15 +171,11 @@ #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 - * use wait() explictly. so we have to disable the signal (which + * use wait() explictly. so we have to reset the signal (which * was inherited from the parent). */ -#ifdef DEBIAN (void) signal(SIGCHLD, SIG_DFL); #else - (void) signal(SIGCHLD, SIG_IGN); -#endif -#else /* on system-V systems, we are ignoring SIGCLD. we have to stop * ignoring it now or the wait() in cron_pclose() won't work. * because of this, we have to wait() for our children here, as well. @@ -245,7 +240,6 @@ PAM_FAIL_CHECK; retcode = pam_open_session(pamh, PAM_SILENT); PAM_FAIL_CHECK; - #endif /* fork again, this time so we can exec the user's command. @@ -288,11 +282,9 @@ /* grandchild process. make std{in,out} be the ends of * pipes opened by our daddy; make stderr go to stdout. */ - /* Closes are unnecessary -- let dup2() do it */ - - /* close(STDIN) */; dup2(stdin_pipe[READ_PIPE], STDIN); + dup2(stdin_pipe[READ_PIPE], STDIN); dup2(fileno(tmpout), STDOUT); - /* close(STDERR)*/; dup2(STDOUT, STDERR); + dup2(STDOUT, STDERR); /* close the pipe we just dup'ed. The resources will remain. @@ -348,26 +340,20 @@ _exit(OK_EXIT); } # endif /*DEBUGGING*/ -#if 0 - { - struct sigaction oact; - sigaction(SIGCHLD, NULL, &oact); - } - fprintf(stdout,"error"); -#endif #ifdef WITH_SELINUX if (is_selinux_enabled() > 0) { if (u->scontext != 0L) { - if (setexeccon(u->scontext) < 0) { - if (security_getenforce() > 0) { - fprintf(stderr, "Could not set exec context to %s for user %s\n", u->scontext,u->name); + if (setexeccon(u->scontext) < 0 && + security_getenforce() > 0) { + fprintf(stderr, "Could not set exec context " + "to %s for user %s\n", + u->scontext,u->name); _exit(ERROR_EXIT); } - } - } - else if(security_getenforce() > 0) - { - fprintf(stderr, "Error, must have a security context for the cron job when in enforcing mode.\nUser %s.\n", u->name); + } else if (security_getenforce() > 0) { + fprintf(stderr, "Error, must have a security context " + "for the cron job when in enforcing " + "mode.\nUser %s.\n", u->name); _exit(ERROR_EXIT); } } @@ -513,7 +499,6 @@ long pos; struct stat mcsb; - int statret; fseek(tmpout, 0, SEEK_END); pos = ftell(tmpout); @@ -531,7 +516,7 @@ goto mail_finished; /* Don't send mail if MAILCMD is not available */ - if ((statret = stat(MAILCMD, &mcsb)) != 0) { + if (stat(MAILCMD, &mcsb) != 0) { Debug(DPROC|DEXT, ("%s not found, not sending mail\n", MAILCMD)) if (pos > 0) { log_it("CRON", getpid(), "info", "No MTA installed, discarding output"); @@ -569,10 +554,10 @@ arpadate(&StartTime)); # endif /* MAIL_DATE */ fprintf(mail, "MIME-Version: 1.0\n"); + if ( content_type == 0L ) { fprintf(mail, "Content-Type: text/plain; charset=%s\n", - cron_default_mail_charset - ); + cron_default_mail_charset); } else { /* user specified Content-Type header. * disallow new-lines for security reasons @@ -581,24 +566,28 @@ char *nl=content_type; size_t ctlen = strlen(content_type); - while( (*nl != '\0') - && ((nl=strchr(nl,'\n')) != 0L) - && (nl < (content_type+ctlen)) - ) *nl = ' '; + while ((*nl != '\0') && + ((nl=strchr(nl,'\n')) != 0L) && + (nl < (content_type+ctlen))) { + *nl = ' '; + } fprintf(mail,"Content-Type: %s\n", content_type); } + if ( content_transfer_encoding != 0L ) { char *nl=content_transfer_encoding; size_t ctlen = strlen(content_transfer_encoding); - while( (*nl != '\0') - && ((nl=strchr(nl,'\n')) != 0L) - && (nl < (content_transfer_encoding+ctlen)) - ) *nl = ' '; - - fprintf(mail,"Content-Transfer-Encoding: %s\n", content_transfer_encoding); + while ((*nl != '\0') && + ((nl = strchr(nl,'\n')) != 0L) && + (nl < (content_transfer_encoding+ctlen))) { + *nl = ' '; } - else + + fprintf(mail,"Content-Transfer-Encoding: %s\n", + content_transfer_encoding); + } else { fprintf(mail, "Content-Transfer-Encoding: 8bit\n"); + } for (env = e->envp; *env; env++) fprintf(mail, "X-Cron-Env: <%s>\n", diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/entry.c cron-3.0pl1/entry.c --- cron-3.0pl1/entry.c 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/entry.c 2019-03-21 20:14:33.000000000 +0100 @@ -108,6 +108,10 @@ */ e = (entry *) calloc(sizeof(entry), sizeof(char)); + if (e == NULL) { + log_it("CRON", getpid(), "OOM", "Out of memory parsing crontab"); + return NULL; + } if (ch == '@') { /* all of these should be flagged and load-limited; i.e., diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/env.c cron-3.0pl1/env.c --- cron-3.0pl1/env.c 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/env.c 2019-03-21 20:14:33.000000000 +0100 @@ -278,7 +278,7 @@ register int len = strlen(name); register char *p, *q; - while ((p = *envp++)) { + while ((p = *envp++) != NULL) { if (!(q = strchr(p, '='))) continue; if ((q - p) == len && !strncmp(p, name, len)) diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/externs.h cron-3.0pl1/externs.h --- cron-3.0pl1/externs.h 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/externs.h 2019-03-21 20:14:33.000000000 +0100 @@ -61,7 +61,6 @@ extern void perror(), exit(), free(); extern char *getenv(), *strcpy(), *strchr(), *strtok(); extern void *malloc(), *realloc(); - # define SIG_T void # define TIME_T long # define PID_T int diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/Makefile cron-3.0pl1/Makefile --- cron-3.0pl1/Makefile 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/Makefile 2019-03-21 20:14:33.000000000 +0100 @@ -57,8 +57,8 @@ #<<need getopt()>> LIBS = $(PAM_LIBS) $(SELINUX_LIBS) $(AUDIT_LIBS) #<<optimize or debug?>> -OPTIM = -O2 -#OPTIM = -g +#OPTIM = -O +OPTIM = -g #<<ATT or BSD or POSIX?>> # (ATT untested) #COMPAT = -DATT @@ -73,17 +73,14 @@ #<<manifest defines>> # Allow override from command line DEBUG_DEFS ?= -DDEBUGGING=0 -# The -DUSE_SIGCHLD is needed for the Alpha port -DEFS = -DDEBIAN -DUSE_SIGCHLD $(DEBUG_DEFS) $(PAM_DEFS) $(SELINUX_DEFS) $(AUDIT_DEFS) +DEFS = $(DEBUG_DEFS) $(PAM_DEFS) $(SELINUX_DEFS) $(AUDIT_DEFS) #(SGI IRIX systems need this) #DEFS = -D_BSD_SIGNALS -Dconst= #<<the name of the BSD-like install program>> #INSTALL = installbsd -INSTALL = install -s +INSTALL = install #<<any special load flags>> -# LDFLAGS = -s -# Let install do the strip - +#LDFLAGS = #################################### end configurable stuff SHELL = /bin/sh diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/misc.c cron-3.0pl1/misc.c --- cron-3.0pl1/misc.c 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/misc.c 2019-03-21 20:14:33.000000000 +0100 @@ -213,8 +213,7 @@ fprintf(stderr, "%s: created\n", CRONDIR); stat(CRONDIR, &sb); } else { - fprintf(stderr, "%s: mkdir: %s\n", CRONDIR, - strerror(errno)); + fprintf(stderr, "%s: mkdir: %s\n", CRONDIR, strerror(errno)); exit(ERROR_EXIT); } (void) umask(um); @@ -331,7 +330,6 @@ /* abandon fd and fp even though the file is open. we need to * keep it open and locked, but we don't need the handles elsewhere. */ - } /* get_char(file) : like getc() but increment LineNumber on newlines @@ -344,7 +342,7 @@ ch = getc(file); if (ch == '\n') - Set_LineNum(LineNumber + 1); + Set_LineNum(LineNumber + 1) return ch; } @@ -358,7 +356,7 @@ { ungetc(ch, file); if (ch == '\n') - Set_LineNum(LineNumber - 1); + Set_LineNum(LineNumber - 1) } @@ -479,7 +477,23 @@ init = TRUE; #if defined(ALLOW_FILE) && defined(DENY_FILE) allow = fopen(ALLOW_FILE, "r"); + if (allow == NULL) { + /* Only if the file does not exist do we ignore the + * error. Otherwise, we deny by default. + */ + if (errno != ENOENT) { + perror(ALLOW_FILE); + return FALSE; + } + } deny = fopen(DENY_FILE, "r"); + if (allow == NULL) { + /* See above */ + if (errno != ENOENT) { + perror(DENY_FILE); + return FALSE; + } + } Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny)) #else allow = NULL; @@ -515,15 +529,14 @@ char *event; char *detail; { -#if defined(LOG_FILE) PID_T pid = xpid; +#if defined(LOG_FILE) char *msg; TIME_T now = time((TIME_T) 0); register struct tm *t = localtime(&now); int msg_size; #endif /*LOG_FILE*/ - #if defined(LOG_FILE) /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. */ @@ -567,15 +580,6 @@ #endif /*LOG_FILE*/ #if defined(SYSLOG) - - - /* we don't use LOG_PID since the pid passed to us by - * our client may not be our own. therefore we want to - * print the pid ourselves. - */ - /* SteveG says: That comment is not consistent with the - code, and makes no sense -- I suspect it's a remnant - of a cut-n-paster... */ # ifdef LOG_CRON openlog(ProgramName, LOG_PID, LOG_CRON); # else @@ -584,7 +588,6 @@ syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail); - closelog(); #endif /*SYSLOG*/ #if DEBUGGING diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/pathnames.h cron-3.0pl1/pathnames.h --- cron-3.0pl1/pathnames.h 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/pathnames.h 2019-03-21 20:14:33.000000000 +0100 @@ -19,7 +19,7 @@ * $Id: pathnames.h,v 1.3 1994/01/15 20:43:43 vixie Exp $ */ -#if (defined(BSD)) && (BSD >= 199103) || defined(__linux__) || defined(AIX) || defined(__GNU__) || defined(__GLIBC__) +#if (defined(BSD)) && (BSD >= 199103) || defined(__linux__) || defined(AIX) || defined(__GLIBC__) # include <paths.h> #endif /*BSD*/ @@ -47,14 +47,9 @@ * LOG_FILE or SYSLOG is defined, we don't log. If * both are defined, we log both ways. */ -#ifdef DEBIAN #define ALLOW_FILE "/etc/cron.allow" /*-*/ #define DENY_FILE "/etc/cron.deny" /*-*/ -#else -#define ALLOW_FILE "allow" /*-*/ -#define DENY_FILE "deny" /*-*/ -#endif -/* #define LOG_FILE "log" -*/ +/* #define LOG_FILE "log" /*-*/ /* where should the daemon stick its PID? */ @@ -67,20 +62,13 @@ /* 4.3BSD-style crontab */ #define SYSCRONTAB "/etc/crontab" -#ifdef DEBIAN /* where package specific crontabs live */ #define SYSCRONDIR "/etc/cron.d" -#endif + /* what editor to use if no EDITOR or VISUAL * environment variable specified. */ -#if defined(DEBIAN) # define EDITOR "/usr/bin/sensible-editor" -#elif defined(_PATH_VI) -# define EDITOR _PATH_VI -#else -# define EDITOR "/usr/ucb/vi" -#endif #ifndef _PATH_BSHELL # define _PATH_BSHELL "/bin/sh" @@ -90,12 +78,11 @@ # define _PATH_DEFPATH "/usr/bin:/bin" #endif -#ifndef _PATH_DEFPATH_ROOT -# define _PATH_DEFPATH_ROOT "/usr/sbin:/usr/bin:/sbin:/bin" +#ifndef REBOOT_FILE +# define REBOOT_FILE "/run/crond.reboot" #endif -#ifdef DEBIAN #ifndef CRONDIR_MODE /* Create mode for CRONDIR; must be in sync with * packaging @@ -114,4 +101,3 @@ */ #define SPOOL_DIR_GROUP "crontab" #endif -#endif diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/popen.c cron-3.0pl1/popen.c --- cron-3.0pl1/popen.c 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/popen.c 2019-03-21 20:14:33.000000000 +0100 @@ -148,7 +148,6 @@ exit(ERROR_EXIT); } chdir(env_get("HOME", e->envp)); - #if WANT_GLOBBING execvp(gargv[0], gargv); #else diff -Nru -w --exclude crontab.1 --exclude crontab.5 --exclude cron.8 cron-3.0pl1/user.c cron-3.0pl1/user.c --- cron-3.0pl1/user.c 2019-03-21 20:14:32.000000000 +0100 +++ cron-3.0pl1/user.c 2019-03-21 20:14:33.000000000 +0100 @@ -58,9 +58,7 @@ freecon(current_con); return (security_getenforce() > 0); } - } - else - { + } else { context_t temp_con = context_new(current_con); if (temp_con == NULL) { log_it(name, getpid(), "context_new FAILED", tabname); @@ -110,8 +108,7 @@ * permission check for this purpose. */ - for(i = 0; i < list_count; i++) - { + for (i = 0; i < list_count; i++) { retval = security_compute_av(context_list[i], file_context, SECCLASS_FILE, @@ -139,7 +136,6 @@ #endif -#ifdef DEBIAN /* Function used to log errors in crontabs from cron daemon. (User crontabs are checked before they're accepted, but system crontabs are not. */ @@ -163,8 +159,6 @@ } } -#endif - void free_user(u) user *u; @@ -236,7 +230,6 @@ } #endif - /* * init environment. this will be copied/augmented for each entry. */ @@ -249,6 +242,7 @@ /* * load the crontab */ + Set_LineNum(1) do { status = load_env(envstr, file); switch (status) { @@ -268,13 +262,9 @@ } goto done; case FALSE: -#ifdef DEBIAN err_user = fname; e = load_entry(file, crontab_error, pw, envp); err_user = NULL; -#else - e = load_entry(file, NULL, pw, envp); -#endif if (e) { e->next = u->crontab; u->crontab = e; @@ -298,7 +288,19 @@ } 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);