From: Christian Kastner <ckk@kvr.at>
Date: Sat, 26 Dec 2015 11:44:24 +0100
Subject: Backport crontab envparser from v4.1

The 3.0 parser is crude and may misparse crontab entries containing equal signs
(=). This patch backports the parser from the 4.1 release.

Bug-Debian: https://bugs.debian.org/437180
Origin: backport, http://ftp.isc.org/isc/cron/cron_4.1.shar
Forwarded: no
Last-Update: 2015-12-26
Index: cron/env.c
===================================================================
--- cron.orig/env.c
+++ cron/env.c
@@ -128,6 +128,17 @@ env_set(envp, envstr)
 	return (p);
 }
 
+/* The following states are used by load_env(), traversed in order: */
+enum env_state {
+	NAMEI,		/* First char of NAME, may be quote */
+	NAME,		/* Subsequent chars of NAME */
+	EQ1,		/* After end of name, looking for '=' sign */
+	EQ2,		/* After '=', skipping whitespace */
+	VALUEI,		/* First char of VALUE, may be quote */
+	VALUE,		/* Subsequent chars of VALUE */
+	FINI,		/* All done, skipping trailing whitespace */
+	ERROR,		/* Error */
+};
 
 /* return	ERR = end of file
  *		FALSE = not an env setting (file was repositioned)
@@ -140,8 +151,9 @@ load_env(envstr, f)
 {
 	long	filepos;
 	int	fileline;
-	char	name[MAX_ENVSTR], val[MAX_ENVSTR];
-	int	fields;
+	enum env_state state;
+	char name[MAX_ENVSTR], val[MAX_ENVSTR];
+	char quotechar, *c, *str;
 
 	filepos = ftell(f);
 	fileline = LineNumber;
@@ -153,20 +165,90 @@ load_env(envstr, f)
 
 	Debug(DPARS, ("load_env, read <%s>\n", envstr))
 
-	name[0] = val[0] = '\0';
-	fields = sscanf(envstr, "%[^ =] = %[^\n#]", name, val);
-	if (fields != 2) {
-		Debug(DPARS, ("load_env, not 2 fields (%d)\n", fields))
+	bzero(name, sizeof name);
+	bzero(val, sizeof val);
+	str = name;
+	state = NAMEI;
+	quotechar = '\0';
+	c = envstr;
+	while (state != ERROR && *c) {
+		switch (state) {
+		case NAMEI:
+		case VALUEI:
+			if (*c == '\'' || *c == '"')
+				quotechar = *c++;
+			state++;
+			/* FALLTHROUGH */
+		case NAME:
+		case VALUE:
+			if (quotechar) {
+				if (*c == quotechar) {
+					state++;
+					c++;
+					break;
+				}
+				if (state == NAME && *c == '=') {
+					state = ERROR;
+					break;
+				}
+			} else {
+				if (state == NAME) {
+					if (isspace((unsigned char)*c)) {
+						c++;
+						state++;
+						break;
+					}
+					if (*c == '=') {
+						state++;
+						break;
+					}
+				}
+			}
+			*str++ = *c++;
+			break;
+
+		case EQ1:
+			if (*c == '=') {
+				state++;
+				str = val;
+				quotechar = '\0';
+			} else {
+				if (!isspace((unsigned char)*c))
+					state = ERROR;
+			}
+			c++;
+			break;
+
+		case EQ2:
+		case FINI:
+			if (isspace((unsigned char)*c))
+				c++;
+			else
+				state++;
+			break;
+
+		default:
+			abort();
+		}
+	}
+	if (state != FINI && !(state == VALUE && !quotechar)) {
+		Debug(DPARS, ("load_env, not an env var, state = %d\n", state))
 		fseek(f, filepos, 0);
 		Set_LineNum(fileline);
 		return (FALSE);
 	}
+	if (state == VALUE) {
+		/* End of unquoted value: trim trailing whitespace */
+		c = val + strlen(val);
+		while (c > val && isspace((unsigned char)c[-1]))
+			*(--c) = '\0';
+	}
 
-	/* 2 fields from scanf; looks like an env setting
-	 */
+	/* 2 fields from parser; looks like an env setting */
 
 	/*
-	 * process value string
+	 * This can't overflow because get_string() limited the size of the
+	 * name and val fields.  Still, it doesn't hurt to be careful...
 	 */
 	/*local*/{
 		int	len = strdtb(val);
@@ -188,7 +270,6 @@ load_env(envstr, f)
 	return (TRUE);
 }
 
-
 char *
 env_get(name, envp)
 	register char	*name;
