diff --git a/SOURCES/0002-Add-random-within-range-operator.patch b/SOURCES/0002-Add-random-within-range-operator.patch
new file mode 100644
index 0000000..45dbf9f
--- /dev/null
+++ b/SOURCES/0002-Add-random-within-range-operator.patch
@@ -0,0 +1,375 @@
+From 8c3f71bbe109f5df8280eeaa2152dabc4f48474a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ond=C5=99ej=20Poho=C5=99elsk=C3=BD?=
+ <35430604+opohorel@users.noreply.github.com>
+Date: Mon, 8 Nov 2021 16:20:09 +0100
+Subject: [PATCH 2/5] Add random within range '~' operator
+
+With the operator one can specify for a job a random time or date within
+a specified range for a field.
+The random value is generated when the crontab where the job is
+specified, is loaded.
+---
+ man/crontab.5 |   9 ++
+ src/entry.c   | 267 +++++++++++++++++++++++++++++++-------------------
+ 2 files changed, 175 insertions(+), 101 deletions(-)
+
+diff --git a/man/crontab.5 b/man/crontab.5
+index 04358cb..5d89862 100644
+--- a/man/crontab.5
++++ b/man/crontab.5
+@@ -205,6 +205,15 @@ hyphen.  The specified range is inclusive.  For example, 8-11 for
+ an 'hours' entry specifies execution at hours 8, 9, 10, and 11. The first
+ number must be less than or equal to the second one.
+ .PP
++Randomization of the execution time within a range can be used.
++A random number within a range specified as two numbers separated with
++a tilde is picked.  The specified range is inclusive.
++For example, 6~15 for a 'minutes' entry picks a random minute
++within 6 to 15 range.  The random number is picked when crontab file is parsed.
++The first number must be less than or equal to the second one. You might omit
++one or both of the numbers specifying the range.  For example, ~ for a 'minutes'
++entry picks a random minute within 0 to 59 range.
++.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".
+ .PP
+diff --git a/src/entry.c b/src/entry.c
+index 36e639e..f2bb717 100644
+--- a/src/entry.c
++++ b/src/entry.c
+@@ -62,9 +62,22 @@ static const char *ecodes[] = {
+ 	"out of memory"
+ };
+ 
++typedef enum {
++	R_START,
++	R_AST,
++	R_STEP,
++	R_TERMS,
++	R_NUM1,
++	R_RANGE,
++	R_RANGE_NUM2,
++	R_RANDOM,
++	R_RANDOM_NUM2,
++	R_FINISH,
++} range_state_t;
++
+ static int get_list(bitstr_t *, int, int, const char *[], int, FILE *),
+-get_range(bitstr_t *, int, int, const char *[], int, FILE *),
+-get_number(int *, int, const char *[], int, FILE *, const char *),
++get_range(bitstr_t *, int, int, const char *[], FILE *),
++get_number(int *, int, const char *[], FILE *),
+ set_element(bitstr_t *, int, int, int);
+ 
+ void free_entry(entry * e) {
+@@ -467,11 +480,14 @@ get_list(bitstr_t * bits, int low, int high, const char *names[],
+ 	/* process all ranges
+ 	 */
+ 	done = FALSE;
++	/* unget ch to allow get_range() to process it properly 
++	 */
++	unget_char(ch, file);
+ 	while (!done) {
+-		if (EOF == (ch = get_range(bits, low, high, names, ch, file)))
++		if (EOF == (ch = get_range(bits, low, high, names, file)))
+ 			return (EOF);
+ 		if (ch == ',')
+-			ch = get_char(file);
++			continue;
+ 		else
+ 			done = TRUE;
+ 	}
+@@ -486,144 +502,193 @@ get_list(bitstr_t * bits, int low, int high, const char *names[],
+ 	return (ch);
+ }
+ 
++inline static int is_separator(int ch) {
++	switch (ch) {
++		case '\t':
++		case '\n':
++		case ' ':
++		case ',':
++			return 1;
++		default:
++			return 0;
++	}
++}
++
++
+ 
+ static int
+ get_range(bitstr_t * bits, int low, int high, const char *names[],
+-	int ch, FILE * file) {
++		FILE * file) {
+ 	/* range = number | number "-" number [ "/" number ]
++	 *         | [number] "~" [number]
+ 	 */
++	
++	int ch, i, num1, num2, num3;
+ 
+-	int i, num1, num2, num3;
++	/* default value for step
++	 */
++	num3 = 1;
++	range_state_t state = R_START;
++
++	while (state != R_FINISH && ((ch = get_char(file)) != EOF)) {
++		switch (state) {
++			case R_START:
++				if (ch == '*') {
++					num1 = low;
++					num2 = high;
++					state = R_AST;
++					break;
++				}
++				if (ch == '~') {
++					num1 = low;
++					state = R_RANDOM;
++					break;
++				}
++				unget_char(ch, file);
++				if (get_number(&num1, low, names, file) != EOF) {
++					state = R_NUM1;
++					break;
++				}
++				return (EOF);
+ 
+-	Debug(DPARS | DEXT, ("get_range()...entering, exit won't show\n"));
++			case R_AST:
++				if (ch == '/') {
++					state = R_STEP;
++					break;
++				}
++				if (is_separator(ch)) {
++					state = R_FINISH;
++					break;
++				}
++				return (EOF);
+ 
+-	if (ch == '*') {
+-		/* '*' means "first-last" but can still be modified by /step
+-		 */
+-		num1 = low;
+-		num2 = high;
+-		ch = get_char(file);
+-		if (ch == EOF)
+-			return (EOF);
+-	}
+-	else {
+-		ch = get_number(&num1, low, names, ch, file, ",- \t\n");
+-		if (ch == EOF)
+-			return (EOF);
++			case R_STEP:
++				if (get_number(&num3, 0, PPC_NULL, file) != EOF) {
++					state = R_TERMS;
++					break;
++				}
++				return (EOF);
+ 
+-		if (ch != '-') {
+-			/* not a range, it's a single number.
+-			 */
+-			if (EOF == set_element(bits, low, high, num1)) {
+-				unget_char(ch, file);
++			case R_TERMS:
++				if (is_separator(ch)) {
++					state = R_FINISH;
++					break;
++				}
+ 				return (EOF);
+-			}
+-			return (ch);
+-		}
+-		else {
+-			/* eat the dash
+-			 */
+-			ch = get_char(file);
+-			if (ch == EOF)
++
++			case R_NUM1:
++				if (ch == '-') {
++					state = R_RANGE;
++					break;
++				}
++				if (ch == '~') {
++					state = R_RANDOM;
++					break;
++				}
++				if (is_separator(ch)) {
++					num2 = num1;
++					state = R_FINISH;
++					break;
++				}
+ 				return (EOF);
+ 
+-			/* get the number following the dash
+-			 */
+-			ch = get_number(&num2, low, names, ch, file, "/, \t\n");
+-			if (ch == EOF || num1 > num2)
++			case R_RANGE:
++				if (get_number(&num2, low, names, file) != EOF) {
++					state = R_RANGE_NUM2;
++					break;
++				}
+ 				return (EOF);
+-		}
+-	}
+ 
+-	/* check for step size
+-	 */
+-	if (ch == '/') {
+-		/* eat the slash
+-		 */
+-		ch = get_char(file);
+-		if (ch == EOF)
+-			return (EOF);
++			case R_RANGE_NUM2:
++				if (ch == '/') {
++					state = R_STEP;
++					break;
++				}
++				if (is_separator(ch)) {
++					state = R_FINISH;
++					break;
++				}
++				return (EOF);
+ 
+-		/* get the step size -- note: we don't pass the
+-		 * names here, because the number is not an
+-		 * element id, it's a step size.  'low' is
+-		 * sent as a 0 since there is no offset either.
+-		 */
+-		ch = get_number(&num3, 0, PPC_NULL, ch, file, ", \t\n");
+-		if (ch == EOF || num3 == 0)
+-			return (EOF);
+-	}
+-	else {
+-		/* no step.  default==1.
+-		 */
+-		num3 = 1;
+-	}
++			case R_RANDOM:
++				if (is_separator(ch)) {
++					num2 = high;
++					state = R_FINISH;
++				}
++				else if (unget_char(ch, file),
++						get_number(&num2, low, names, file) != EOF) {
++					state = R_TERMS;
++				}
++				/* fail if couldn't find match on previous term
++				 */
++				else
++					return (EOF);
+ 
+-	/* num1 (through i) will be validated by set_element() below, but num2
+-	 * and num3 are merely used as loop condition and increment, and must
+-	 * be validated separately.
+-	 */
+-	if (num2 < low || num2 > high || num3 > high)
++				/* if invalid random range was selected */
++				if (num1 > num2)
++					return (EOF);
++
++				/* select random number in range <num1, num2>
++				 */
++				num1 = num2 = random() % (num2 - num1 + 1) + num1;
++				break;
++
++
++			default:
++				/* We should never get here
++				 */
++				return (EOF);
++		}
++	}
++	if (state != R_FINISH || ch == EOF)
+ 		return (EOF);
+ 
+-	/* range. set all elements from num1 to num2, stepping
+-	 * by num3.  (the step is a downward-compatible extension
+-	 * proposed conceptually by bob@acornrc, syntactically
+-	 * designed then implemented by paul vixie).
+-	 */
+ 	for (i = num1; i <= num2; i += num3)
+ 		if (EOF == set_element(bits, low, high, i)) {
+ 			unget_char(ch, file);
+ 			return (EOF);
+ 		}
+-
+-	return (ch);
++	return ch;
+ }
+ 
+ static int
+-get_number(int *numptr, int low, const char *names[], int ch, FILE * file,
+-	const char *terms) {
++get_number(int *numptr, int low, const char *names[], FILE * file) {
+ 	char temp[MAX_TEMPSTR], *pc;
+-	int len, i;
++	int len, i, ch;
++	char *endptr;
+ 
+ 	pc = temp;
+ 	len = 0;
+ 
+-	/* first look for a number */
+-	while (isdigit((unsigned char) ch)) {
++	/* get all alnum characters available */
++	while (isalnum((ch = get_char(file)))) {
+ 		if (++len >= MAX_TEMPSTR)
+ 			goto bad;
+ 		*pc++ = (char)ch;
+-		ch = get_char(file);
+ 	}
+-	*pc = '\0';
+-	if (len != 0) {
+-		/* got a number, check for valid terminator */
+-		if (!strchr(terms, ch))
+-			goto bad;
+-		*numptr = atoi(temp);
+-		return (ch);
++	if (len == 0)
++		goto bad;
++
++	unget_char(ch, file);
++
++	/* try to get number */
++	*numptr = (int) strtol(temp, &endptr, 10);
++	if (*endptr == '\0' && temp != endptr) {
++		/* We have a number */
++		return 0;
+ 	}
+ 
+ 	/* no numbers, look for a string if we have any */
+ 	if (names) {
+-		while (isalpha((unsigned char) ch)) {
+-			if (++len >= MAX_TEMPSTR)
+-				goto bad;
+-			*pc++ = (char)ch;
+-			ch = get_char(file);
+-		}
+-		*pc = '\0';
+-		if (len != 0 && strchr(terms, ch)) {
+-			for (i = 0; names[i] != NULL; i++) {
+-				Debug(DPARS | DEXT,
+-					("get_num, compare(%s,%s)\n", names[i], temp));
+-				if (!strcasecmp(names[i], temp)) {
+-					*numptr = i + low;
+-					return (ch);
+-				}
++		for (i = 0; names[i] != NULL; i++) {
++			Debug(DPARS | DEXT, ("get_num, compare(%s,%s)\n", names[i], temp));
++			if (strcasecmp(names[i], temp) == 0) {
++				*numptr = i + low;
++				return 0;
+ 			}
+ 		}
++	} else {
++		goto bad;
+ 	}
+ 
+   bad:
+-- 
+2.36.1
+
diff --git a/SOURCES/0003-get_number-Add-missing-NUL-termination-for-the-scann.patch b/SOURCES/0003-get_number-Add-missing-NUL-termination-for-the-scann.patch
new file mode 100644
index 0000000..9faa394
--- /dev/null
+++ b/SOURCES/0003-get_number-Add-missing-NUL-termination-for-the-scann.patch
@@ -0,0 +1,25 @@
+From 0589b06aa369efd3cd5dfc0bba9a868f48a14506 Mon Sep 17 00:00:00 2001
+From: Tomas Mraz <tmraz@fedoraproject.org>
+Date: Wed, 5 Jan 2022 19:17:18 +0100
+Subject: [PATCH 3/5] get_number: Add missing NUL termination for the scanned
+ string
+
+---
+ src/entry.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/entry.c b/src/entry.c
+index f2bb717..15ce9b5 100644
+--- a/src/entry.c
++++ b/src/entry.c
+@@ -666,6 +666,7 @@ get_number(int *numptr, int low, const char *names[], FILE * file) {
+ 			goto bad;
+ 		*pc++ = (char)ch;
+ 	}
++	*pc = '\0';
+ 	if (len == 0)
+ 		goto bad;
+ 
+-- 
+2.36.1
+
diff --git a/SOURCES/0004-Fix-regression-in-handling-x-crontab-entries.patch b/SOURCES/0004-Fix-regression-in-handling-x-crontab-entries.patch
new file mode 100644
index 0000000..3ad494f
--- /dev/null
+++ b/SOURCES/0004-Fix-regression-in-handling-x-crontab-entries.patch
@@ -0,0 +1,28 @@
+From 991a5f2a44c68f576b6c6da3a7ac8fbc8f97a3b0 Mon Sep 17 00:00:00 2001
+From: Tomas Mraz <tmraz@fedoraproject.org>
+Date: Tue, 22 Mar 2022 14:35:48 +0100
+Subject: [PATCH 4/5] Fix regression in handling */x crontab entries
+
+Fixes #102
+---
+ src/entry.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/entry.c b/src/entry.c
+index 15ce9b5..e9e258b 100644
+--- a/src/entry.c
++++ b/src/entry.c
+@@ -563,7 +563,9 @@ get_range(bitstr_t * bits, int low, int high, const char *names[],
+ 				return (EOF);
+ 
+ 			case R_STEP:
+-				if (get_number(&num3, 0, PPC_NULL, file) != EOF) {
++				unget_char(ch, file);
++				if (get_number(&num3, 0, PPC_NULL, file) != EOF
++				    && num3 != 0) {
+ 					state = R_TERMS;
+ 					break;
+ 				}
+-- 
+2.36.1
+
diff --git a/SOURCES/0005-Fix-regression-in-handling-1-5-crontab-entries.patch b/SOURCES/0005-Fix-regression-in-handling-1-5-crontab-entries.patch
new file mode 100644
index 0000000..6152ef6
--- /dev/null
+++ b/SOURCES/0005-Fix-regression-in-handling-1-5-crontab-entries.patch
@@ -0,0 +1,24 @@
+From d1a4e2b1a091df104881a6dcd0e41d805c86cb1a Mon Sep 17 00:00:00 2001
+From: w30023233 <wangyuhang27@huawei.com>
+Date: Wed, 23 Mar 2022 15:40:01 +0800
+Subject: [PATCH 5/5] Fix regression in handling 1-5 crontab entries
+
+---
+ src/entry.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/entry.c b/src/entry.c
+index e9e258b..bb7cb62 100644
+--- a/src/entry.c
++++ b/src/entry.c
+@@ -595,6 +595,7 @@ get_range(bitstr_t * bits, int low, int high, const char *names[],
+ 				return (EOF);
+ 
+ 			case R_RANGE:
++				unget_char(ch, file);
+ 				if (get_number(&num2, low, names, file) != EOF) {
+ 					state = R_RANGE_NUM2;
+ 					break;
+-- 
+2.36.1
+
diff --git a/SPECS/cronie.spec b/SPECS/cronie.spec
index 81cc235..6bdcef2 100644
--- a/SPECS/cronie.spec
+++ b/SPECS/cronie.spec
@@ -6,12 +6,17 @@
 Summary:   Cron daemon for executing programs at set times
 Name:      cronie
 Version:   1.5.7
-Release:   5%{?dist}
+Release:   8%{?dist}
 License:   MIT and BSD and ISC and GPLv2+
 URL:       https://github.com/cronie-crond/cronie
 Source0:   https://github.com/cronie-crond/cronie/releases/download/cronie-%{version}/cronie-%{version}.tar.gz
 
 Patch:     0001-Address-issues-found-by-coverity-scan.patch
+# Add support for "~" ("random within range") + regression fixing patches (rhbz#2090691)
+Patch:     0002-Add-random-within-range-operator.patch
+Patch:     0003-get_number-Add-missing-NUL-termination-for-the-scann.patch
+Patch:     0004-Fix-regression-in-handling-x-crontab-entries.patch
+Patch:     0005-Fix-regression-in-handling-1-5-crontab-entries.patch
 
 Requires:  dailyjobs
 
@@ -187,7 +192,7 @@ exit 0
 %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/pam.d/crond
 %endif
 %config(noreplace) %{_sysconfdir}/sysconfig/crond
-%config(noreplace) %{_sysconfdir}/cron.deny
+%config(noreplace,missingok) %{_sysconfdir}/cron.deny
 %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/cron.d/0hourly
 %attr(0644,root,root) /usr/lib/systemd/system/crond.service
 
@@ -206,6 +211,17 @@ exit 0
 %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/cron.d/dailyjobs
 
 %changelog
+* Mon Jul 11 2022 Jan Staněk <jstanek@redhat.com> - 1.5.7-8
+- Set 'missingok' for /etc/cron.deny to not recreate it on update
+
+* Tue May 31 2022 Ondřej Pohořelský <opohorel@redhat.com> - 1.5.7-7
+- Add gating.yaml
+  Related: rhbz#2090691
+
+* Fri May 27 2022 Ondřej Pohořelský <opohorel@redhat.com> - 1.5.7-6
+- Add support for "~" ("random within range")
+  Resolves: rhbz#2090691
+
 * Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 1.5.7-5
 - Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
   Related: rhbz#1991688