cups/0001-Updated-cupsFileGetConf-and-cupsFilePutConf-to-escap.patch
RHEL Packaging Agent 16fa047311 Fix CVE-2026-34980: control character injection in option values
Resolves: RHEL-177945

Co-Authored: Zdenek Dohnal <zdohnal@redhat.com>
2026-06-23 15:37:24 +02:00

363 lines
9.3 KiB
Diff

From c5ce534c03d0698509f3eb7ff90ca41610625570 Mon Sep 17 00:00:00 2001
From: Michael R Sweet <msweet@msweet.org>
Date: Thu, 19 Feb 2026 18:44:33 -0500
Subject: [PATCH] Updated and to escape more characters.
---
cups/file.c | 147 +++++++++++++++++++++++++++++++++---------------
cups/testfile.c | 63 +++++++++++++++++++--
3 files changed, 161 insertions(+), 50 deletions(-)
diff --git a/cups/file.c b/cups/file.c
index 7f48706335..a053368c82 100644
--- a/cups/file.c
+++ b/cups/file.c
@@ -725,11 +725,9 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */
* Range check input...
*/
- DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT
- ", value=%p, linenum=%p)", (void *)fp, (void *)buf, CUPS_LLCAST buflen, (void *)value, (void *)linenum));
+ DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT ", value=%p, linenum=%p)", (void *)fp, (void *)buf, CUPS_LLCAST buflen, (void *)value, (void *)linenum));
- if (!fp || (fp->mode != 'r' && fp->mode != 's') ||
- !buf || buflen < 2 || !value)
+ if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2 || !value)
{
if (value)
*value = NULL;
@@ -748,28 +746,30 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */
(*linenum) ++;
/*
- * Strip any comments...
+ * Handle escaped characters and strip any comments...
*/
- if ((ptr = strchr(buf, '#')) != NULL)
+ for (ptr = buf; *ptr; ptr ++)
{
- if (ptr > buf && ptr[-1] == '\\')
+ if (*ptr == '#')
{
- // Unquote the #...
- _cups_strcpy(ptr - 1, ptr);
+ // Strip comment text...
+ *ptr = '\0';
+ break;
}
- else
+ else if (*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '#' || ptr[1] == 'n' || ptr[1] == 'r'))
{
- // Strip the comment and any trailing whitespace...
- while (ptr > buf)
- {
- if (!_cups_isspace(ptr[-1]))
- break;
+ /*
+ * \\, \#, \n, or \r, remove backslash and update the escaped char as
+ * needed...
+ */
- ptr --;
- }
+ _cups_strcpy(ptr, ptr + 1);
- *ptr = '\0';
+ if (*ptr == 'n')
+ *ptr = '\n';
+ else if (*ptr == 'r')
+ *ptr = '\r';
}
}
@@ -777,7 +777,11 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */
* Strip leading whitespace...
*/
- for (ptr = buf; _cups_isspace(*ptr); ptr ++);
+ for (ptr = buf; *ptr; ptr ++)
+ {
+ if (!_cups_isspace(*ptr))
+ break;
+ }
if (ptr > buf)
_cups_strcpy(buf, ptr);
@@ -793,14 +797,16 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */
*/
for (ptr = buf; *ptr; ptr ++)
+ {
if (_cups_isspace(*ptr))
break;
+ }
if (*ptr)
{
/*
* Have a value, skip any other spaces...
- */
+ */
while (_cups_isspace(*ptr))
*ptr++ = '\0';
@@ -810,12 +816,14 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */
/*
* Strip trailing whitespace and > for lines that begin with <...
- */
+ */
ptr += strlen(ptr) - 1;
if (buf[0] == '<' && *ptr == '>')
+ {
*ptr-- = '\0';
+ }
else if (buf[0] == '<' && *ptr != '>')
{
/*
@@ -1497,19 +1505,18 @@ cupsFilePutChar(cups_file_t *fp, /* I - CUPS file */
/*
* 'cupsFilePutConf()' - Write a configuration line.
*
- * This function handles any comment escaping of the value.
+ * This function handles any escaping of the value.
*
* @since CUPS 1.4/macOS 10.6@
*/
ssize_t /* O - Number of bytes written or -1 on error */
cupsFilePutConf(cups_file_t *fp, /* I - CUPS file */
- const char *directive, /* I - Directive */
- const char *value) /* I - Value */
+ const char *directive, /* I - Directive */
+ const char *value) /* I - Value */
{
ssize_t bytes, /* Number of bytes written */
temp; /* Temporary byte count */
- const char *ptr; /* Pointer into value */
if (!fp || !directive || !*directive)
@@ -1518,34 +1525,84 @@ cupsFilePutConf(cups_file_t *fp, /* I - CUPS file */
if ((bytes = cupsFilePuts(fp, directive)) < 0)
return (-1);
- if (cupsFilePutChar(fp, ' ') < 0)
- return (-1);
- bytes ++;
-
if (value && *value)
{
- if ((ptr = strchr(value, '#')) != NULL)
+ const char *start, // Start of current fragment
+ *ptr; // Pointer into value
+
+ if (cupsFilePutChar(fp, ' ') < 0)
+ return (-1);
+ bytes ++;
+
+ for (start = ptr = value; *ptr; ptr ++)
+ {
+ if (strchr("#\\\n\r", *ptr) != NULL)
+ {
+ /*
+ * Character that needs to be escaped...
+ */
+
+ if (ptr > start)
+ {
+ /*
+ * Write unescaped portion...
+ */
+
+ if ((temp = cupsFileWrite(fp, start, (size_t)(ptr - start))) < 0)
+ return (-1);
+
+ bytes += temp;
+ }
+
+ start = ptr + 1;
+
+ if (*ptr == '\\')
+ {
+ /*
+ * "\" (for escaping)
+ */
+
+ if (cupsFilePuts(fp, "\\\\") < 0)
+ return (-1);
+ }
+ else if (*ptr == '#')
+ {
+ /*
+ * "#" (for comment)
+ */
+
+ if (cupsFilePuts(fp, "\\#") < 0)
+ return (-1);
+ }
+ else if (*ptr == '\n')
+ {
+ /*
+ * LF
+ */
+
+ if (cupsFilePuts(fp, "\\n") < 0)
+ return (-1);
+ }
+ else if (cupsFilePuts(fp, "\\r") < 0)
+ {
+ return (-1);
+ }
+
+ bytes += 2;
+ }
+ }
+
+ if (ptr > start)
{
/*
- * Need to quote the first # in the info string...
+ * Write remaining unescaped portion...
*/
- if ((temp = cupsFileWrite(fp, value, (size_t)(ptr - value))) < 0)
- return (-1);
- bytes += temp;
-
- if (cupsFilePutChar(fp, '\\') < 0)
- return (-1);
- bytes ++;
+ if ((temp = cupsFileWrite(fp, start, (size_t)(ptr - start))) < 0)
+ return (-1);
- if ((temp = cupsFilePuts(fp, ptr)) < 0)
- return (-1);
bytes += temp;
}
- else if ((temp = cupsFilePuts(fp, value)) < 0)
- return (-1);
- else
- bytes += temp;
}
if (cupsFilePutChar(fp, '\n') < 0)
diff --git a/cups/testfile.c b/cups/testfile.c
index 4f8fbefaf4..97293f9399 100644
--- a/cups/testfile.c
+++ b/cups/testfile.c
@@ -552,6 +552,17 @@ read_write_tests(int compression) /* I - Use compression? */
off_t length; /* Length of file */
static const char *partial_line = "partial line";
/* Partial line */
+ static const char * const values[] = /* cupsFileGet/PutConf values */
+ {
+ "simple",
+ "'single-quoted value'",
+ "\"double-quoted value\"",
+ "value with # comment",
+ "value with \n newline",
+ "value with \r carriage return",
+ "value with \\ backslash",
+ "pathological\r\nvalue\\#with comment"
+ };
/*
@@ -628,6 +639,27 @@ read_write_tests(int compression) /* I - Use compression? */
status ++;
}
+ /*
+ * cupsFilePutConf()
+ */
+
+ fputs("cupsFilePutConf(): ", stdout);
+ for (i = 0; i < (int)(sizeof(values) / sizeof(values[0])); i ++)
+ {
+ if (cupsFilePutConf(fp, "TestConf", values[i]) < 0)
+ break;
+ }
+
+ if (i >= (int)(sizeof(values) / sizeof(values[0])))
+ {
+ puts("PASS");
+ }
+ else
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ }
+
/*
* cupsFilePutChar()
*/
@@ -684,11 +716,11 @@ read_write_tests(int compression) /* I - Use compression? */
fputs("cupsFileTell(): ", stdout);
- if ((length = cupsFileTell(fp)) == 81933283)
+ if ((length = cupsFileTell(fp)) == 81933542)
puts("PASS");
else
{
- printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length);
+ printf("FAIL (" CUPS_LLFMT " instead of 81933542)\n", CUPS_LLCAST length);
status ++;
}
@@ -766,7 +798,7 @@ read_write_tests(int compression) /* I - Use compression? */
linenum = 1;
- fputs("cupsFileGetConf(): ", stdout);
+ fputs("cupsFileGetConf(TestLine): ", stdout);
for (i = 0, value = NULL; i < 1000; i ++)
if (!cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
@@ -789,6 +821,27 @@ read_write_tests(int compression) /* I - Use compression? */
status ++;
}
+ fputs("cupsFileGetConf(TestConf): ", stdout);
+
+ for (i = 0; i < (int)(sizeof(values) / sizeof(values[0])); i ++)
+ {
+ if (!cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+ {
+ printf("FAIL (%s)\n", strerror(errno));
+ status ++;
+ break;
+ }
+ else if (_cups_strcasecmp(line, "TestConf") || !value || strcmp(value, values[i]))
+ {
+ printf("FAIL (Expected 'TestConf %s', got '%s %s')\n", values[i], line, value);
+ status ++;
+ break;
+ }
+ }
+
+ if (i >= (int)(sizeof(values) / sizeof(values[0])))
+ puts("PASS");
+
/*
* cupsFileGetChar()
*/
@@ -869,11 +922,11 @@ read_write_tests(int compression) /* I - Use compression? */
fputs("cupsFileTell(): ", stdout);
- if ((length = cupsFileTell(fp)) == 81933283)
+ if ((length = cupsFileTell(fp)) == 81933542)
puts("PASS");
else
{
- printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length);
+ printf("FAIL (" CUPS_LLFMT " instead of 81933542)\n", CUPS_LLCAST length);
status ++;
}