xmlrpc-c/SOURCES/0011-Fix-HTML-injection-CVE-2026-15928.patch
2026-09-07 22:47:21 -04:00

238 lines
7.5 KiB
Diff

From 59478dd8977985c7e431698a76da9263bd102016 Mon Sep 17 00:00:00 2001
From: RHEL Packaging Agent <redhat-ymir-agent@redhat.com>
Date: Tue, 4 Aug 2026 09:02:45 +0000
Subject: [PATCH] Fix HTML injection -- putting URI or file name containing
HTML control characters inside HTML response
Backport of upstream SVN revision 3342.
CVE-2026-15928
---
lib/abyss/src/handler.c | 42 +++++++++++++++++--
lib/abyss/src/html.c | 87 ++++++++++++++++++++++++++++++++++++++++
lib/abyss/src/html.h | 7 ++++
lib/abyss/src/response.c | 7 +++-
4 files changed, 138 insertions(+), 5 deletions(-)
create mode 100644 lib/abyss/src/html.c
create mode 100644 lib/abyss/src/html.h
diff --git a/lib/abyss/src/handler.c b/lib/abyss/src/handler.c
index 80a2914..98431eb 100644
--- a/lib/abyss/src/handler.c
+++ b/lib/abyss/src/handler.c
@@ -36,6 +36,7 @@
#include "file.h"
#include "conn.h"
#include "http.h"
+#include "html.h"
#include "date.h"
#include "abyss_info.h"
@@ -291,11 +292,15 @@ sendDirectoryDocumentHeading(TSession * const sessionP,
"------------------------------------"
"--------------------------------------------\r\n");
} else {
+ const char * const escapedUri = html_escapedForHtml(uri);
+
sprintf(z, "<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD><BODY>"
"<H1>Index of %s</H1><PRE>",
- uri, uri);
+ escapedUri, escapedUri);
strcat(z, "Name Size "
"Date-Time Type<HR WIDTH=100%>\r\n");
+
+ xmlrpc_strfree(escapedUri);
}
HTTPWriteBodyChunk(sessionP, z, strlen(z));
@@ -303,6 +308,37 @@ sendDirectoryDocumentHeading(TSession * const sessionP,
+static void
+formatHtmlForDirEntry(char * const buffer,
+ const char * const name,
+ int const attrib,
+ const char * const z1,
+ const char * const z2,
+ const char * const z3,
+ const char * const z4,
+ const char * const p) {
+
+ const char * const escapedName = html_escapedForHtml(name);
+ const char * const escapedZ1 = html_escapedForHtml(z1);
+ const char * const escapedZ2 = html_escapedForHtml(z2);
+ const char * const escapedZ3 = html_escapedForHtml(z3);
+ const char * const escapedZ4 = html_escapedForHtml(z4);
+ const char * const escapedP = html_escapedForHtml(p);
+
+ sprintf(buffer, "<A HREF=\"%s%s\">%s</A>%s %s %s %s\r\n",
+ escapedName, attrib & A_SUBDIR ? "/" : "",
+ escapedZ1, escapedP, escapedZ3, escapedZ2, escapedZ4);
+
+ xmlrpc_strfree(escapedP);
+ xmlrpc_strfree(escapedZ1);
+ xmlrpc_strfree(escapedZ2);
+ xmlrpc_strfree(escapedZ3);
+ xmlrpc_strfree(escapedZ4);
+ xmlrpc_strfree(escapedName);
+}
+
+
+
static void
sendDirectoryDocument(TList * const listP,
bool const ascending,
@@ -405,9 +441,7 @@ sendDirectoryDocument(TList * const listP,
if (text)
sprintf(z, "%s%s %s %s %s\r\n", z1, p, z3, z2, z4);
else
- sprintf(z, "<A HREF=\"%s%s\">%s</A>%s %s %s %s\r\n",
- fi->name, fi->attrib & A_SUBDIR ? "/" : "",
- z1, p, z3, z2, z4);
+ formatHtmlForDirEntry(z, fi->name, fi->attrib, z1, z2, z3, z4, p);
HTTPWriteBodyChunk(sessionP, z, strlen(z));
}
diff --git a/lib/abyss/src/html.c b/lib/abyss/src/html.c
new file mode 100644
index 0000000..58f085f
--- /dev/null
+++ b/lib/abyss/src/html.c
@@ -0,0 +1,87 @@
+/*=============================================================================
+ html
+===============================================================================
+ This module contains utilities for processing HTML.
+=============================================================================*/
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <xmlrpc-c/string_int.h>
+
+#include "html.h"
+
+
+
+static void
+insertCharEntity(char * const buffer,
+ unsigned int * const cursorP,
+ const char * const name) {
+
+ size_t i;
+
+ buffer[(*cursorP)++] = '&';
+
+ for (i = 0; i < strlen(name); ++i)
+ buffer[(*cursorP)++] = name[i];
+
+ buffer[(*cursorP)++] = ';';
+}
+
+
+
+const char *
+html_escapedForHtml(const char * const plainText) {
+/*----------------------------------------------------------------------------
+ The text 'plainText' with HTML control characters replaced with
+ HTML character entities so that the result can be used inside an HTML
+ document without any chance of interfering with HTML controls.
+
+ In particular, we substitute one of the following for each associated
+ character:
+
+ &amp;
+ &lt;
+ &gt;
+ "
+ &apos;
+
+ We return newly malloced storage or a constant if we can't get allocate
+ enough, like 'xmlrpc_strdupsol', which must be freed with 'xmlrpc_strfree'.
+-----------------------------------------------------------------------------*/
+
+ size_t const maxPossibleReturnSz = strlen(plainText) * 6 + 1;
+
+ const char * retval;
+ char * buffer; /* malloc'ed */
+
+ buffer = malloc(maxPossibleReturnSz);
+
+ if (!buffer)
+ retval = xmlrpc_strnomemval();
+ else {
+ unsigned int inCursor, outCursor;
+
+ for (inCursor = 0, outCursor = 0;
+ inCursor < strlen(plainText);
+ ++inCursor) {
+
+ switch (plainText[inCursor]) {
+ case '&': insertCharEntity(buffer, &outCursor, "amp"); break;
+ case '<': insertCharEntity(buffer, &outCursor, "lt"); break;
+ case '>': insertCharEntity(buffer, &outCursor, "gt"); break;
+ case '"': insertCharEntity(buffer, &outCursor, "quot"); break;
+ case '\'': insertCharEntity(buffer, &outCursor, "apos"); break;
+ default:
+ buffer[outCursor++] = plainText[inCursor];
+ }
+ }
+
+ buffer[outCursor++] = '\0'; /* string-terminating NUL */
+
+ assert(outCursor <= maxPossibleReturnSz);
+
+ retval = buffer;
+ }
+ return retval;
+}
diff --git a/lib/abyss/src/html.h b/lib/abyss/src/html.h
new file mode 100644
index 0000000..97235f6
--- /dev/null
+++ b/lib/abyss/src/html.h
@@ -0,0 +1,7 @@
+#ifndef HTML_H_INCLUDED
+#define HTML_H_INCLUDED
+
+const char *
+html_escapedForHtml(const char * const plainText);
+
+#endif
diff --git a/lib/abyss/src/response.c b/lib/abyss/src/response.c
index cc0980a..5cf145d 100644
--- a/lib/abyss/src/response.c
+++ b/lib/abyss/src/response.c
@@ -34,6 +34,7 @@
#include "data.h"
#include "abyss_info.h"
#include "http.h"
+#include "html.h"
@@ -41,6 +42,8 @@ void
ResponseError2(TSession * const sessionP,
const char * const explanation) {
+ const char * const escapedExplanation = html_escapedForHtml(explanation);
+
const char * errorDocument;
ResponseAddField(sessionP, "Content-type", "text/html");
@@ -54,11 +57,13 @@ ResponseError2(TSession * const sessionP,
"<P>%s</P>" SERVER_HTML_INFO
"</BODY>"
"</HTML>",
- sessionP->status, sessionP->status, explanation);
+ sessionP->status, sessionP->status, escapedExplanation);
ConnWrite(sessionP->connP, errorDocument, strlen(errorDocument));
xmlrpc_strfree(errorDocument);
+
+ xmlrpc_strfree(escapedExplanation);
}