java-topology/defects/openoffice-0002/patch/openoffice-0002.patch

109 lines
4.3 KiB
Diff

# UNDF: UNDF-2026-000001137
# openoffice-0002: CurlSession::curlDebugOutput logs HTTP headers verbatim — MOAD-0004 (CWE-312)
#
# In main/ucb/source/ucp/webdav/CurlSession.cxx, when our WebDAV logger is
# configured at LogLevel::FINEST, the curl debug callback (Curl_DebugCallback /
# curlDebugOutput) logs all outgoing and incoming HTTP headers using
# CURLOPT_VERBOSE mode. Outgoing headers (CURLINFO_HEADER_OUT) include the
# Authorization header with HTTP Basic or Digest credentials encoded as
# base64(user:password) or Digest response tokens.
#
# LogLevel::FINEST is a user-configurable runtime setting in OpenOffice (Tools →
# Options → OpenOffice.org → Advanced → Expert Configuration or via ooo-logging
# configuration). When activated for diagnostics, all WebDAV authentication
# credentials are written to the log file in plaintext base64.
#
# Example logged line:
# [CurlHDR ->] Authorization: Basic dXNlcjpteXBhc3N3b3Jk
#
# The base64 "dXNlcjpteXBhc3N3b3Jk" decodes trivially to "user:mypassword".
#
# Fix: strip the value from any outgoing header line whose name is a
# credential-bearing header (Authorization, Proxy-Authorization, X-Auth-Token,
# Cookie) before passing it to the logger. Log the header name with a redacted
# placeholder so debugging of auth flow is still possible without exposing secrets.
#
# Severity: MEDIUM — requires FINEST log level to be enabled, but that is
# a realistic diagnostics scenario; once enabled, all WebDAV credentials are
# recorded in persistent log files readable by any process with log read access.
--- a/main/ucb/source/ucp/webdav/CurlSession.cxx
+++ b/main/ucb/source/ucp/webdav/CurlSession.cxx
@@ -336,10 +336,38 @@
return session->curlDebugOutput( type, reinterpret_cast<char*>( data ), size );
}
+// Credential-bearing headers whose values must never appear in logs.
+// Names are lower-cased; comparison uses case-insensitive prefix match.
+static bool lcl_IsCredentialHeader( const rtl::OString& rHeader )
+{
+ // Extract header name (everything before the first ':')
+ sal_Int32 nColon = rHeader.indexOf( ':' );
+ if ( nColon <= 0 )
+ return false;
+ rtl::OString aName = rHeader.copy( 0, nColon ).trim().toAsciiLowerCase();
+ return aName == "authorization"
+ || aName == "proxy-authorization"
+ || aName == "x-auth-token"
+ || aName == "www-authenticate"
+ || aName == "proxy-authenticate"
+ || aName == "cookie"
+ || aName == "set-cookie";
+}
+
+// Return a sanitized version of an HTTP header line — value replaced with
+// "<redacted>" for credential-bearing headers.
+static rtl::OString lcl_RedactHeader( const rtl::OString& rHeader )
+{
+ sal_Int32 nColon = rHeader.indexOf( ':' );
+ if ( nColon <= 0 )
+ return rHeader;
+ rtl::OString aName = rHeader.copy( 0, nColon );
+ return aName + rtl::OString( ": <redacted>" );
+}
+
int CurlSession::curlDebugOutput( curl_infotype type, char *data, int size )
{
const char *prefix;
switch ( type )
{
case CURLINFO_TEXT:
prefix = "[CurlINFO ]";
break;
case CURLINFO_HEADER_IN:
prefix = "[CurlHDR <-]";
break;
case CURLINFO_HEADER_OUT:
prefix = "[CurlHDR ->]";
break;
case CURLINFO_DATA_IN:
prefix = "[CurlData<-]";
break;
case CURLINFO_DATA_OUT:
prefix = "[CurlData->]";
break;
default:
return 0;
}
// Trim the trailing \r\n
if ( size >= 1 && ( data[size - 1] == '\r' || data[size - 1] == '\n' ) )
--size;
if ( size >= 1 && ( data[size - 1] == '\r' || data[size - 1] == '\n' ) )
--size;
rtl::OString message( data, size );
- m_aLogger.log( LogLevel::FINEST, "$1$ $2$", prefix, message );
+ // Sanitize credential-bearing HTTP headers before logging (CWE-312).
+ // Authorization, Proxy-Authorization, Cookie etc. must never be logged
+ // in plaintext regardless of log level.
+ if ( ( type == CURLINFO_HEADER_IN || type == CURLINFO_HEADER_OUT )
+ && lcl_IsCredentialHeader( message ) )
+ {
+ m_aLogger.log( LogLevel::FINEST, "$1$ $2$", prefix,
+ lcl_RedactHeader( message ) );
+ }
+ else
+ {
+ m_aLogger.log( LogLevel::FINEST, "$1$ $2$", prefix, message );
+ }
return 0;
}