RHEL-93944 RFE: Add Allow list for FoomaticRIPCommandLine PPD values [rhel-10]

Resolves: RHEL-93944
This commit is contained in:
Zdenek Dohnal 2025-10-01 16:27:27 +02:00
parent 30151e07d7
commit 7679ea82cd
4 changed files with 1746 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ Summary: OpenPrinting CUPS filters for CUPS 2.X
Name: cups-filters
Epoch: 1
Version: 2.0.0
Release: 10%{?dist}
Release: 11%{?dist}
# the CUPS exception text is the same as LLVM exception, so using that name with
# agreement from legal team
@ -31,6 +31,15 @@ Source2: lftocrlf
# Patches
# introducing foomatic-hash, but without rejecting values in foomatic-rip
# https://github.com/OpenPrinting/cups-filters/pull/648
Patch001: 0001-Introduce-foomatic-hash-and-reject-unauthorized-valu.patch
# make sure errors from foomatic-rip are propagated
# https://github.com/OpenPrinting/cups-filters/pull/649
Patch002: foomatic-ripdie-error.patch
# rejecting the unknown values in foomatic-rip
# https://github.com/OpenPrinting/cups-filters/pull/648
Patch003: foomaticrip-reject-unknown-values.patch
# driverless backend/driver was moved into a separate package to
@ -63,6 +72,8 @@ BuildRequires: pkgconfig(libcupsfilters) >= 2.0b3
BuildRequires: pkgconfig(libppd) >= 2.0b3
# Make sure we get postscriptdriver tags.
BuildRequires: python3-cups
# for systemd unit for upgrade
BuildRequires: systemd-rpm-macros
%if %{with braille}
Recommends: braille-printer-app
@ -132,6 +143,68 @@ queues.
install -p -m 0755 %{SOURCE2} %{buildroot}%{_cups_serverbin}/filter/lftocrlf
install -p -m 0644 %{SOURCE1} %{buildroot}%{_datadir}/ppd/cupsfilters/lftocrlf.ppd
# for post upgrade script to allow already existing printers
mkdir -p %{buildroot}%{_libexecdir}/%{name}
# the script which does the deed - goes PPD file by PPD file if there are any
# problematic PPD options, scans the values into file in /var/tmp for review, while
# hashed values are saved in a file in directory where foomatic-rip reads it.
#
# hashes.new is for marking this script already run once, so it won't be run again.
# It is needed to prevent allowing newly installed printers after another upgrade.
# This is handled by systemd unit.
cat > %{buildroot}%{_libexecdir}/%{name}/posttrans.sh << EOF
#!/usr/bin/bash
if \$(grep -q -R 'FoomaticRIPCommandLine\|FoomaticRipOptionSetting' %{_sysconfdir}/cups/ppd)
then
tmpfile=\$(mktemp -p /var/tmp foomatic-scan.XXXXXXXX)
for ppd in %{_sysconfdir}/cups/ppd/*.ppd
do
foomatic-hash --ppd \$ppd \$tmpfile %{_sysconfdir}/foomatic/hashes.d/hashes.upgrade || :
done
if test -f %{_sysconfdir}/foomatic/hashes.d/hashes.upgrade
then
echo "Foomatic-rip values which can inject code found - review findings in \$tmpfile. Read release notes for instructions." || :
fi
else
touch %{_sysconfdir}/foomatic/hashes.d/hashes.new
fi
exit 0
EOF
mkdir -p %{buildroot}%{_unitdir}
# Upgrade service which will be run only when the specified directory is empty
# - the service will be run only once, on the first upgrade introducing the change.
cat > %{buildroot}%{_unitdir}/foomaticrip-upgrade.service << EOF
[Unit]
Description=Allowing already installed printers for foomatic-rip
ConditionPathIsDirectory=%{_sysconfdir}/foomatic/hashes.d
ConditionDirectoryNotEmpty=!%{_sysconfdir}/foomatic/hashes.d
[Service]
Type=oneshot
ExecStart=bash -c %{_libexecdir}/%{name}/posttrans.sh
[Install]
WantedBy=multi-user.target
EOF
mkdir -p %{buildroot}%{_unitdir}/cups.service.d
# We have to make sure the upgrade service is run before starting cupsd, so the allowed
# hashes are in place already and there is no printing disruption.
cat > %{buildroot}%{_unitdir}/cups.service.d/10-foomaticrip-upgrade.conf << EOF
[Unit]
After=foomaticrip-upgrade.service
Wants=foomaticrip-upgrade.service
EOF
# LSB3.2 requires /usr/bin/foomatic-rip,
# create it temporarily as a relative symlink
# we may use symlink to universal filter, but LSB is about guaranteed compatibility set
@ -166,11 +239,29 @@ if [ $1 -gt 1 ]
then
rm -f /var/cache/cups/ppds.dat || :
fi
%systemd_post foomaticrip-upgrade.service
%preun
%systemd_preun foomaticrip-upgrade.service
%postun
%systemd_postun foomaticrip-upgrade.service
%posttrans
%systemd_posttrans_with_reload foomaticrip-upgrade.service
if [ $1 -gt 1 ]
then
systemctl start foomaticrip-upgrade.service || :
fi
%files
%license COPYING LICENSE NOTICE
%doc AUTHORS ABOUT-NLS CHANGES.md CONTRIBUTING.md DEVELOPING.md README.md
%{_bindir}/foomatic-hash
%{_bindir}/foomatic-rip
%attr(0744,root,root) %{_cups_serverbin}/backend/beh
# all backends needs to be run only as root because of kerberos
@ -208,6 +299,8 @@ fi
%{_datadir}/cups/mime/cupsfilters.convs
%{_datadir}/cups/mime/cupsfilters-universal-postscript.convs
%{_datadir}/cups/mime/cupsfilters-universal.convs
%dir %{_datadir}/foomatic
%dir %{_datadir}/foomatic/hashes.d
%{_datadir}/ppd/cupsfilters
%if %{with cups_ppdc}
# escp.h and pcl.h are required during runtime, because
@ -220,7 +313,15 @@ fi
%{_datadir}/ppdc/escp.h
%{_datadir}/ppdc/pcl.h
%endif
%dir %{_libexecdir}/%{name}
%attr(0744,root,root) %{_libexecdir}/%{name}/posttrans.sh
%{_mandir}/man1/foomatic-hash.1.gz
%{_mandir}/man1/foomatic-rip.1.gz
%config(noreplace) %{_sysconfdir}/foomatic
%ghost %attr(0644,root,root) %{_sysconfdir}/foomatic/hashes.d/hashes.new
%dir %{_unitdir}/cups.service.d
%{_unitdir}/cups.service.d/10-foomaticrip-upgrade.conf
%{_unitdir}/foomaticrip-upgrade.service
%files driverless
%license COPYING LICENSE NOTICE
@ -234,6 +335,9 @@ fi
%changelog
* Tue Sep 30 2025 Zdenek Dohnal <zdohnal@redhat.com> - 1:2.0.0-11
- RHEL-93944 RFE: Add Allow list for FoomaticRIPCommandLine PPD values [rhel-10]
* Wed Jul 23 2025 Zdenek Dohnal <zdohnal@redhat.com> - 1:2.0.0-10
- RHEL-83060 lpinfo -m doesn't show textonly driver

View File

@ -0,0 +1,13 @@
diff --git a/filter/foomatic-rip/util.c b/filter/foomatic-rip/util.c
index 508bc09..ad79fbf 100644
--- a/filter/foomatic-rip/util.c
+++ b/filter/foomatic-rip/util.c
@@ -76,7 +76,7 @@ rip_die(int status,
{
va_list ap;
- _log("Process is dying with \"");
+ _log("ERROR: Process is dying with \"");
va_start(ap, msg);
_logv(msg, ap);
va_end(ap);

View File

@ -0,0 +1,188 @@
From 41c5f2f6139e4d3693c2483ee4281202a80ae451 Mon Sep 17 00:00:00 2001
From: zdohnal <zdohnal@redhat.com>
Date: Tue, 22 Jul 2025 15:12:19 +0200
Subject: [PATCH] Introduce foomatic-hash and reject unauthorized values in
foomatic-rip (#648)
The change provides a way for users to have control over what values are
allowed for the foomatic-rip-related PPD keywords FoomaticRIPCommandLine,
FoomaticRIPCommandLinePDF, and FoomaticRIPOptionSetting. Since the
values can be later used when constructing a shell command, the filter
foomatic-rip was a target of several exploits (caused by issues at
different places in CUPS or in different projects of the printing stack) to
do arbitrary code execution when the filter is used.
By default the filter is run by user lp, so the issue is mitigated, but
this PR gives admin complete control over what can be run in
foomatic-rip and reject anything injected into system via different
ways.
First, the new tool - foomatic-hash - can be called on a PPD file or
directory with drivers/PPDs, with scan output and file with hexadecimal
representation of hashed values. Once the scan output is reviewed by
admin, admin can decide to put the resulting hashes into
/etc/foomatic/hashes.d and allow them for the filter.
---
Makefile.am | 44 ++-
README.md | 23 ++
configure.ac | 2 +-
filter/foomatic-rip/foomatic-hash.1 | 66 ++++
filter/foomatic-rip/foomatic-hash.c | 549 ++++++++++++++++++++++++++
filter/foomatic-rip/foomatic-rip.1.in | 16 +
filter/foomatic-rip/foomaticrip.c | 75 ----
filter/foomatic-rip/foomaticrip.h | 40 --
filter/foomatic-rip/options.c | 67 ++++
filter/foomatic-rip/process.c | 9 +
filter/foomatic-rip/process.h | 3 +
filter/foomatic-rip/util.c | 341 +++++++++++++++-
filter/foomatic-rip/util.h | 67 ++++
13 files changed, 1178 insertions(+), 124 deletions(-)
create mode 100644 filter/foomatic-rip/foomatic-hash.1
create mode 100644 filter/foomatic-rip/foomatic-hash.c
diff --git a/filter/foomatic-rip/foomatic-rip.1.in b/filter/foomatic-rip/foomatic-rip.1.in
index 9685a95f5..3dff5215f 100644
--- a/filter/foomatic-rip/foomatic-rip.1.in
+++ b/filter/foomatic-rip/foomatic-rip.1.in
@@ -193,6 +193,15 @@ friends. Several PPD files use shell constructs that require a more
modern shell like \fBbash\fR, \fBzsh\fR, or \fBksh\fR.
+.SH PPD OPTION VALUE RESTRICTIONS AND EXCEPTIONS
+
+The values of PPD options \fBFoomaticRIPCommandLine\fR, \fBFoomaticRIPCommandLinePDF\fR and \fBFoomaticRIPOptionSetting\fR
+are rejected in the default configuration because of security implications. Users can use the tool \fBfoomatic-hash(1)\fR, which provides
+values of affected PPD options from found drivers and hashes of those values in hexadecimal format. User is expected to review the found values,
+and if there is nothing suspicious in the output, copy the file with hashes into into the directory \fB@sysconfdir@/foomatic/hashes.d\fR
+to allow the exceptions for found values.
+
+
.SH FILES
.PD 0
.TP 0
@@ -209,6 +218,13 @@ The PPD files of the currently defined printers
Configuration file for foomatic-rip
+.TP 0
+@sysconfdir@/foomatic/hashes.d
+.TP 0
+@datadir@/foomatic/hashes.d
+
+Directories with hashes of allowed values
+
.PD 0
.\".SH SEE ALSO
diff --git a/filter/foomatic-rip/options.c b/filter/foomatic-rip/options.c
index bad833bc1..032fe9ec3 100644
--- a/filter/foomatic-rip/options.c
+++ b/filter/foomatic-rip/options.c
@@ -102,6 +102,42 @@ get_icc_profile_for_qualifier(const char **qualifier)
}
+//
+// 'is_allowed_value' - Check if the option value is allowed.
+//
+
+int // O - Boolean value - true 1 / false 0
+is_allowed_value(cups_array_t *ar, // I - Array of already known hashes from system
+ char *value, // I - Scanned value from PPD file
+ size_t value_len) // I - Value length
+{
+ char hash_string[65]; // Help array to store hexadecimal hashed string
+
+ //
+ // Empty string is allowed...
+ //
+
+ if (!value_len)
+ return (1);
+
+ //
+ // Hash the value and get hexadecimal string for it...
+ //
+
+ if (hash_data((unsigned char*)value, value_len, hash_string, sizeof(hash_string)))
+ return (0);
+
+ //
+ // Check if the found hexadecimal hashed string is in the array -> allowed on the system...
+ //
+
+ if (cupsArrayFind(ar, hash_string))
+ return (1);
+
+ return (0);
+}
+
+
// a selector is a general tri-dotted specification.
// The 2nd and 3rd elements of the qualifier are optionally modified by
// cupsICCQualifier2 and cupsICCQualifier3:
@@ -1866,12 +1902,19 @@ read_ppd_file(const char *filename)
option_t *opt, *current_opt = NULL;
param_t *param;
icc_mapping_entry_t *entry;
+ cups_array_t *known_hashes = NULL;
fh = fopen(filename, "r");
if (!fh)
rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Unable to open PPD file %s\n", filename);
_log("Parsing PPD file ...\n");
+ if (load_system_hashes(&known_hashes))
+ {
+ fclose(fh);
+ rip_die(EXIT_PRNERR_NORETRY, "Not enough memory for array allocation\n.");
+ }
+
dstrassure(value, 256);
qualifier_data = list_create();
@@ -1955,10 +1998,26 @@ read_ppd_file(const char *filename)
}
else if (strcmp(key, "FoomaticRIPCommandLine") == 0)
{
+ if (!is_allowed_value(known_hashes, value->data, strlen(value->data)))
+ {
+ cupsArrayDelete(known_hashes);
+ fclose(fh);
+
+ rip_die(EXIT_PRNERR_NOTALLOWED, "ERROR: The value of the key %s is not among the allowed values - see foomatic-rip man page for more instructions.\n", key);
+ }
+
unhtmlify(cmd, 4096, value->data);
}
else if (strcmp(key, "FoomaticRIPCommandLinePDF") == 0)
{
+ if (!is_allowed_value(known_hashes, value->data, strlen(value->data)))
+ {
+ cupsArrayDelete(known_hashes);
+ fclose(fh);
+
+ rip_die(EXIT_PRNERR_NOTALLOWED, "ERROR: The value of the key %s is not among the allowed values - see foomatic-rip man page for more instructions.\n", key);
+ }
+
unhtmlify(cmd_pdf, 4096, value->data);
}
else if (!strcmp(key, "cupsFilter"))
@@ -2097,6 +2156,14 @@ read_ppd_file(const char *filename)
}
else if (!strcmp(key, "FoomaticRIPOptionSetting"))
{
+ if (!is_allowed_value(known_hashes, value->data, strlen(value->data)))
+ {
+ cupsArrayDelete(known_hashes);
+ fclose(fh);
+
+ rip_die(EXIT_PRNERR_NOTALLOWED, "ERROR: The value of the key %s is not among the allowed values - see foomatic-rip man page for more instructions.\n", key);
+ }
+
// "*FoomaticRIPOptionSetting <option>[=<choice>]: <code>
// For boolean options <choice> is not given
option_set_choice(assure_option(name),
--
2.50.1