diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.gitignore b/.gitignore index 25ef1e2..a7e11d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,72 +1 @@ -/cups-filters-1.0.20.tar.xz -/cups-filters-1.0.22.tar.xz -/cups-filters-1.0.23.tar.xz -/cups-filters-1.0.24.tar.xz -/cups-filters-1.0.25.tar.xz -/cups-filters-1.0.28.tar.xz -/cups-filters-1.0.29.tar.xz -/cups-filters-1.0.30.tar.xz -/cups-filters-1.0.31.tar.xz -/cups-filters-1.0.32.tar.xz -/cups-filters-1.0.33.tar.xz -/cups-filters-1.0.34.tar.xz -/cups-filters-1.0.35.tar.xz -/cups-filters-1.0.36.tar.xz -/cups-filters-1.0.37.tar.xz -/cups-filters-1.0.38.tar.xz -/cups-filters-1.0.39.tar.xz -/cups-filters-1.0.40.tar.xz -/cups-filters-1.0.41.tar.xz -/cups-filters-1.0.42.tar.xz -/cups-filters-1.0.43.tar.xz -/cups-filters-1.0.44.tar.xz -/cups-filters-1.0.45.tar.xz -/cups-filters-1.0.46.tar.xz -/cups-filters-1.0.47.tar.xz -/cups-filters-1.0.48.tar.xz -/cups-filters-1.0.49.tar.xz -/cups-filters-1.0.50.tar.xz -/cups-filters-1.0.51.tar.xz -/cups-filters-1.0.52.tar.xz -/cups-filters-1.0.53.tar.xz -/cups-filters-1.0.54.tar.xz -/cups-filters-1.0.55.tar.xz -/cups-filters-1.0.58.tar.xz -/cups-filters-1.0.59.tar.xz -/cups-filters-1.0.60.tar.xz -/cups-filters-1.0.61.tar.xz -/cups-filters-1.0.65.tar.xz -/cups-filters-1.0.66.tar.xz -/cups-filters-1.0.67.tar.xz -/cups-filters-1.0.68.tar.xz -/cups-filters-1.0.69.tar.xz -/cups-filters-1.0.70.tar.xz -/cups-filters-1.0.71.tar.xz -/cups-filters-1.0.73.tar.xz -/cups-filters-1.0.74.tar.xz -/cups-filters-1.0.75.tar.xz -/cups-filters-1.0.76.tar.xz -/cups-filters-1.1.0.tar.xz -/cups-filters-1.2.0.tar.xz -/cups-filters-1.3.0.tar.xz -/cups-filters-1.4.0.tar.xz -/cups-filters-1.5.0.tar.xz -/cups-filters-1.6.0.tar.xz -/cups-filters-1.7.0.tar.xz -/cups-filters-1.8.0.tar.xz -/cups-filters-1.8.1.tar.xz -/cups-filters-1.8.2.tar.xz -/cups-filters-1.8.3.tar.xz -/cups-filters-1.9.0.tar.xz -/cups-filters-1.10.0.tar.xz -/cups-filters-1.11.2.tar.xz -/cups-filters-1.11.3.tar.xz -/cups-filters-1.11.4.tar.xz -/cups-filters-1.11.5.tar.xz -/cups-filters-1.11.6.tar.xz -/cups-filters-1.12.0.tar.xz -/cups-filters-1.13.0.tar.xz -/cups-filters-1.13.1.tar.xz -/cups-filters-1.13.2.tar.xz -/cups-filters-1.13.3.tar.xz -/cups-filters-1.13.4.tar.xz +/cups-filters-*.tar.gz diff --git a/0001-Fix-build-failure-with-GCC-15-and-std-c23.patch b/0001-Fix-build-failure-with-GCC-15-and-std-c23.patch new file mode 100644 index 0000000..1a18671 --- /dev/null +++ b/0001-Fix-build-failure-with-GCC-15-and-std-c23.patch @@ -0,0 +1,27 @@ +From 44f59a1aa74c48515d8feba5a61b7ea3aaa592c4 Mon Sep 17 00:00:00 2001 +From: Zdenek Dohnal +Date: Fri, 24 Jan 2025 09:44:58 +0100 +Subject: [PATCH] Fix build failure with GCC 15 and -std=c23 + +The newest standard has more strict data type checks, function pointers +in function prototypes have to declare data types of its arguments. +--- + filter/foomatic-rip/process.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/filter/foomatic-rip/process.h b/filter/foomatic-rip/process.h +index f6e15f65c..54a42923a 100644 +--- a/filter/foomatic-rip/process.h ++++ b/filter/foomatic-rip/process.h +@@ -18,7 +18,7 @@ + #include + + +-pid_t start_process(const char *name, int (*proc_func)(), void *user_arg, ++pid_t start_process(const char *name, int (*proc_func)(FILE*, FILE*, void*), void *user_arg, + FILE **fdin, FILE **fdout); + pid_t start_system_process(const char *name, const char *command, FILE **fdin, + FILE **fdout); +-- +2.48.1 + diff --git a/0001-Introduce-foomatic-hash-and-reject-unauthorized-valu.patch b/0001-Introduce-foomatic-hash-and-reject-unauthorized-valu.patch new file mode 100644 index 0000000..a40fc5e --- /dev/null +++ b/0001-Introduce-foomatic-hash-and-reject-unauthorized-valu.patch @@ -0,0 +1,1485 @@ +From 41c5f2f6139e4d3693c2483ee4281202a80ae451 Mon Sep 17 00:00:00 2001 +From: zdohnal +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/Makefile.am b/Makefile.am +index f4ff7a1a3..fff4d06f7 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -188,6 +188,8 @@ endif + if ENABLE_FOOMATIC + pkgfilter_PROGRAMS += \ + foomatic-rip ++bin_PROGRAMS = \ ++ foomatic-hash + endif + if ENABLE_UNIVERSAL_CUPS_FILTER + pkgfilter_PROGRAMS += \ +@@ -278,6 +280,19 @@ commandtopclx_LDADD = \ + $(LIBPPD_LIBS) \ + $(CUPS_LIBS) + ++noinst_LTLIBRARIES = libfoomatic-util.la ++libfoomatic_util_la_SOURCES = \ ++ filter/foomatic-rip/util.c \ ++ filter/foomatic-rip/util.h \ ++ filter/foomatic-rip/process.c \ ++ filter/foomatic-rip/process.h ++libfoomatic_util_la_CFLAGS = \ ++ -DSYS_HASH_PATH='"$(datadir)/foomatic/hashes.d"' \ ++ -DUSR_HASH_PATH='"$(sysconfdir)/foomatic/hashes.d"' \ ++ $(CUPS_CFLAGS) ++libfoomatic_util_la_LIBADD = \ ++ $(CUPS_LIBS) ++ + foomatic_rip_SOURCES = \ + filter/foomatic-rip/foomaticrip.c \ + filter/foomatic-rip/foomaticrip.h \ +@@ -287,24 +302,34 @@ foomatic_rip_SOURCES = \ + filter/foomatic-rip/pdf.h \ + filter/foomatic-rip/postscript.c \ + filter/foomatic-rip/postscript.h \ +- filter/foomatic-rip/process.c \ +- filter/foomatic-rip/process.h \ + filter/foomatic-rip/renderer.c \ + filter/foomatic-rip/renderer.h \ + filter/foomatic-rip/spooler.c \ +- filter/foomatic-rip/spooler.h \ +- filter/foomatic-rip/util.c \ +- filter/foomatic-rip/util.h ++ filter/foomatic-rip/spooler.h + foomatic_rip_CFLAGS = \ + -DCONFIG_PATH='"$(sysconfdir)/foomatic"' \ + $(CUPS_CFLAGS) \ + $(LIBCUPSFILTERS_CFLAGS) \ +- $(LIBPPD_CFLAGS) ++ $(LIBPPD_CFLAGS) \ ++ -I/$(srcdir)/filter/foomatic-rip/ + foomatic_rip_LDADD = \ + $(CUPS_LIBS) \ + -lm \ + $(LIBCUPSFILTERS_LIBS) \ +- $(LIBPPD_LIBS) ++ $(LIBPPD_LIBS) \ ++ libfoomatic-util.la ++ ++foomatic_hash_SOURCES = \ ++ filter/foomatic-rip/foomatic-hash.c ++foomatic_hash_CFLAGS = \ ++ $(CUPS_CFLAGS) \ ++ $(LIBPPD_CFLAGS) \ ++ -I/$(srcdir)/filter/foomatic-rip/ ++foomatic_hash_LDADD = \ ++ $(CUPS_LIBS) \ ++ -lm \ ++ $(LIBPPD_LIBS) \ ++ libfoomatic-util.la + + gstoraster_SOURCES = \ + filter/gstoraster.c +@@ -576,6 +601,7 @@ man_MANS += $(driverlessmanpages) + endif + + foomaticmanpages = \ ++ filter/foomatic-rip/foomatic-hash.1 \ + filter/foomatic-rip/foomatic-rip.1 + if ENABLE_FOOMATIC + man_MANS += $(foomaticmanpages) +@@ -615,6 +641,8 @@ install-exec-hook: + $(INSTALL) -d -m 755 $(DESTDIR)$(pkgbackenddir) + if ENABLE_FOOMATIC + $(LN_SRF) $(DESTDIR)$(pkgfilterdir)/foomatic-rip $(DESTDIR)$(bindir) ++ $(INSTALL) -d -m 755 $(DESTDIR)$(datadir)/foomatic/hashes.d ++ $(INSTALL) -d -m 755 $(DESTDIR)$(sysconfdir)/foomatic/hashes.d + endif + if ENABLE_DRIVERLESS + $(LN_SRF) $(DESTDIR)$(pkgppdgendir)/driverless $(DESTDIR)$(bindir) +@@ -626,6 +654,8 @@ endif + uninstall-hook: + if ENABLE_FOOMATIC + $(RM) $(DESTDIR)$(bindir)/foomatic-rip ++ $(RMDIR) $(DESTDIR)$(datadir)/foomatic/hashes.d ++ $(RMDIR) $(DESTDIR)$(sysconfdir)/foomatic/hashes.d + endif + if ENABLE_DRIVERLESS + $(RM) $(DESTDIR)$(bindir)/driverless +diff --git a/README.md b/README.md +index 10e66aee5..ce521bcb0 100644 +--- a/README.md ++++ b/README.md +@@ -263,6 +263,29 @@ this filter are the same as for texttopdf (see below) as the texttops + filter calls the texttopdf filter plus Ghostscript's pdf2ps. + + ++### Tool FOOMATIC-HASH and allowing values for foomatic-rip filter ++ ++Several CVEs for printing stack exploited a different security issue ++to craft a PPD which would call the filter foomatic-rip, and provided ++malicious values for PPD options FoomaticRIPCommandLine, FoomaticRIPCommandLinePDF, ++and FoomaticRIPOptionSetting, because the filter constructs a command ++out of the values and runs it in shell under user lp. ++ ++To mitigate the issue, foomatic-rip now allows only values which are allowed ++by admin, and the tool foomatic-hash was invented. The tool scans PPD file or ++a path for drivers with affected values, and generates two files - the first ++with found values for admin to review, and the second with hashes of unique ++values present in the scanned file or path. If admin reviews the found values ++and finds them correct, the found values will be allowed once the file with ++hashes is moved into the directory /etc/foomatic/hashes.d. ++ ++The filter foomatic-rip reads files with allowed hashes from two directories - ++/etc/foomatic/hashes.d and /usr/share/foomatic/hashes.d. The former is meant ++for hashes allowed by the local admin, the latter is for printer driver projects ++to put there files with hashes of values which are present in their project ++after the values are reviewed. ++ ++ + ### Filters + + +diff --git a/configure.ac b/configure.ac +index 3fed334bc..2d4b147e2 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -120,7 +120,7 @@ PKG_CHECK_MODULES([LIBCUPSFILTERS], [libcupsfilters]) + # ================ + # Check for libppd + # ================ +-PKG_CHECK_MODULES([LIBPPD], [libppd]) ++PKG_CHECK_MODULES([LIBPPD], [libppd], [AC_DEFINE(HAVE_LIBPPD, 1, [Have LIBPPD?])], [AC_MSG_RESULT([not found])]) + + # ====================== + # Check system functions +diff --git a/filter/foomatic-rip/foomatic-hash.1 b/filter/foomatic-rip/foomatic-hash.1 +new file mode 100644 +index 000000000..d53919838 +--- /dev/null ++++ b/filter/foomatic-rip/foomatic-hash.1 +@@ -0,0 +1,66 @@ ++.\" ++.\" foomatic-hash man page. ++.\" ++.\" Copyright @ 2025 by Zdenek Dohnal. ++.\" ++.\" Licensed under Apache License v2.0. See the file "LICENSE" for more ++.\" information. ++.\" ++ ++ ++.TH "foomatic-hash" "1" "2025-06-18" "User Commands" ++ ++.SH "NAME" ++ ++foomatic-hash - tool for scanning provided drivers for problematic PPD options and hash them using SHA-256 ++ ++.SH "SYNOPSIS" ++ ++.BI \fBfoomatic-hash\fR\ \fB--ppd\fR\ \fI\fR\ \fI\fR\ \fI\fR ++ ++.BI \fBfoomatic-hash\fR\ \fB--ppd-paths\fR\ \fI\fR\ \fI\fR\ \fI\fR ++ ++ ++.SH "DESCRIPTION" ++ ++The tool scans the provided drivers for values of PPD keywords \fBFoomaticRIPCommandLine\fR, \fBFoomaticRIPCommandLinePDF\fR, and \fBFoomaticRIPOptionSetting\fR, puts the found values into a file for review, and prints out values hashes in hexadecimal format. The hashes are required for allowing the filter \fBfoomatic-rip\fR to process those values. ++ ++ ++.SH "OPTIONS" ++ ++The tool \fBfoomatic-hash\fR supports two options: ++ ++.TP 10 ++.BI \fB--ppd\fR\ \fI\fR ++The tool scans the specific PPD file. ++ ++.TP 10 ++.BI \fB--ppd-paths\fR\ \fI\fR ++The tool scans directories \fIpath1\fR, \fIpath2\fR until \fIpathN\fR for values of desired PPD keyword. Paths are absolute, symlinks are ignored. Each path is divided by comma. LibPPD support is required for the functionality. ++ ++.SH "EXAMPLES" ++Scans PPD file \fBtest.ppd\fR, prints found values into \fBfound_values\fR, hash them and save them into \fBhashed_values\fR. ++.nf ++ ++ foomatic-hash --ppd test.ppd found_values hashed_values ++ ++.fi ++ ++Scans path \fB/etc/cups/ppd\fR for drivers, finds values if any, puts them into \fBfound_values\fR, and hashes them into \fBhashed_values\fR. ++.nf ++ ++ sudo foomatic-hash --ppd-paths /etc/cups/ppd found_value hashed_values ++.fi ++ ++.SH "EXIT STATUS" ++ ++Returns zero if scan happens successfully, non-zero return value for any error during the process. ++ ++ ++.SH "SEE ALSO" ++ ++.BR foomatic-rip (1) ++ ++ ++.BR ++.EL +diff --git a/filter/foomatic-rip/foomatic-hash.c b/filter/foomatic-rip/foomatic-hash.c +new file mode 100644 +index 000000000..6e563870f +--- /dev/null ++++ b/filter/foomatic-rip/foomatic-hash.c +@@ -0,0 +1,549 @@ ++// ++// foomatic-hash.c ++// ++// Copyright (C) 2024-2025 Zdenek Dohnal ++// Copyright (C) 2008 Till Kamppeter ++// Copyright (C) 2008 Lars Karlitski (formerly Uebernickel) ++// ++// This file implements the tool foomatic-hash, which scans presented drivers ++// for FoomaticRIP* option values which are used during composing shell command, prints ++// them into a file for review, hashes the found values and puts them into a separate file. ++// The options in question: ++// - FoomaticRIPCommandLine, ++// - FoomaticRIPCommandLinePDF, ++// - FoomaticRIPOptionSetting. ++// ++// Licensed under Apache License v2.0. See the file "LICENSE" for more ++// information. ++// ++ ++#include "util.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(HAVE_LIBPPD) ++#include ++#endif // HAVE_LIBPPD ++ ++ ++void write_array(cups_array_t *ar, char *filename); ++ ++ ++// ++// `write_array()` - Writes the CUPS array content into file, line by line... ++// ++ ++void ++write_array(cups_array_t *ar, // I - CUPS array with contents to write ++ char *filename) // I - Path to file where to put data in ++{ ++ cups_file_t *f = NULL; // CUPS file pointer ++ ++ if (cupsArrayCount(ar) == 0) ++ return; ++ ++ if ((f = cupsFileOpen(filename, "w")) == NULL) ++ { ++ fprintf(stderr, "Cannot open file \"%s\" for write.\n", filename); ++ return; ++ } ++ ++ for (char *s = (char*)cupsArrayGetFirst(ar); s; s = (char*)cupsArrayGetNext(ar)) ++ cupsFilePrintf(f, "%s\n", s); ++ ++ cupsFileClose(f); ++} ++ ++ ++// ++// 'generate_hash_file()' - Generate file with unique hashes. ++// ++ ++int // O - 0 - success/ 1 - error ++generate_hash_file(cups_array_t *values, // I - File with values to hash ++ char *output) // I - File where to save new file ++{ ++ cups_array_t *syshashes = NULL, // Already existing hashes on system ++ *hashes = NULL; // Hashed values from input ++ char *data = NULL, // Pointer for storing string from array of values ++ comment[16], // Array for storing comment ++ hash_string[65]; // Array for hexadecimal representation of hashed value ++ ++ ++ // ++ // Load existing hashes from system... ++ // ++ ++ if (load_system_hashes(&syshashes)) ++ return (1); ++ ++ // ++ // Load hashes from previous runs if any... ++ // ++ ++ if (load_array(&hashes, output)) ++ return (1); ++ ++ // ++ // Now do the hashing, save the hexadecimal string if it is ++ // unique - if the hash is not on system or in the loaded hash ++ // file from previous runs... ++ // ++ ++ for (data = (char*)cupsArrayGetFirst(values); data; data = (char*)cupsArrayGetNext(values)) ++ { ++ if (hash_data((unsigned char*)data, strlen(data), hash_string, sizeof(hash_string))) ++ return (1); ++ ++ if (!cupsArrayFind(syshashes, hash_string) && !cupsArrayFind(hashes, hash_string)) ++ cupsArrayAdd(hashes, hash_string); ++ } ++ ++ if (cupsArrayCount(hashes)) ++ { ++ // ++ // Add comment mentioning the used hash algorithm ++ // ++ ++ snprintf(comment, sizeof(comment), "# %s", hash_alg); ++ ++ if (!cupsArrayFind(hashes, comment)) ++ cupsArrayAdd(hashes, comment); ++ ++ // ++ // Create a new hash file... ++ // ++ ++ write_array(hashes, output); ++ } ++ ++ cupsArrayDelete(syshashes); ++ cupsArrayDelete(hashes); ++ ++ return (0); ++} ++ ++ ++// ++// `find_foomaticrip_keywords()` - reads PPD file, find FoomaticRIPCommandLine, ++// FoomaticRIPCommandLinePDF and FoomaticRIPOptionSetting, save their values ++// into CUPS array. ++// ++ ++void ++find_foomaticrip_keywords(cups_array_t *data, // O - Array with values of FoomaticRIP* PPD keywords ++ cups_file_t *file) // I - File descriptor opened via CUPS API ++{ ++ char *p; // Helper pointer ++ char key[128], // PPD keyword ++ line[256], // PPD line length is max 255 (excl. \0) ++ name[64], // PPD option name ++ text[64]; // PPD option human-readable text ++ ++ // ++ // Allocate struct for saving value data dynamically, ++ // it can span over multiplelines... ++ // ++ ++ dstr_t *value = create_dstr(); ++ ++ dstrassure(value, 256); ++ ++ // ++ // Going through the PPD file... ++ // ++ ++ while (cupsFileGets(file, line, 256) != NULL) ++ { ++ // ++ // Ignore commmented lines and whatever not starting with '*' ++ // to get the closest keyword ++ // ++ ++ if (line[0] != '*' || startswith(line, "*%")) ++ continue; ++ ++ // ++ // Get the PPD keyword ++ // Structure of PPD line: ++ // *keyword [option_name/option_text]: value1 [value2 value3...] ++ // ++ ++ key[0] = name[0] = text[0] = '\0'; ++ ++ if ((p = strchr(line, ':')) == NULL) ++ continue; ++ ++ *p = '\0'; ++ ++ sscanf(line, "*%127s%*[ \t]%63[^ \t/=)]%*1[/=]%63[^\n]", key, name, text); ++ ++ // ++ // Get the value... ++ // ++ ++ dstrclear(value); ++ sscanf(p + 1, " %255[^\r\n]", value->data); ++ value->len = strlen(value->data); ++ ++ // ++ // If the value is multiline (the current line ends with && or does not end with \"), ++ // continue saving it, and handle quotes if the value is quoted... ++ // ++ ++ while (1) ++ { ++ if (dstrendswith(value, "&&")) ++ { ++ // ++ // "&&" is the continue-on-next-line marker ++ // ++ ++ value->len -= 2; ++ value->data[value->len] = '\0'; ++ } ++ else if (value->data[0] == '\"' && !strchr(value->data +1, '\"')) ++ { ++ // ++ // Quoted but quotes are not yet closed - typically value blocks ++ // ended by keyword *End - append LF for the next line... ++ // ++ ++ dstrcat(value, "\n"); // keep newlines in quoted string ++ } ++ // Quotes already closed, we have the whole value... ++ else ++ break; ++ ++ // ++ // We read the next line if the value was not complete... ++ // ++ ++ if (cupsFileGets(file, line, 256) == NULL) ++ break; ++ ++ dstrcat(value, line); ++ dstrremovenewline(value); ++ ++ // ++ // 2047 characters to read for value sounds reasonable, ++ // break if we have more and crop the string... ++ // ++ ++ if (strlen(value->data) > 2047) ++ { ++ value->data[2047] = '\0'; ++ value->len = 2047; ++ break; ++ } ++ } ++ ++ // ++ // Skip if the key is not what we look for... ++ // ++ ++ if (strcmp(key, "FoomaticRIPCommandLine") && strcmp(key, "FoomaticRIPCommandLinePDF") && strcmp(key, "FoomaticRIPOptionSetting")) ++ continue; ++ ++ // ++ // Remove quotes... ++ // ++ ++ if (value->data[0] == '\"') ++ { ++ memmove(value->data, value->data +1, value->len +1); ++ p = strrchr(value->data, '\"'); ++ if (!p) ++ { ++ fprintf(stderr, "Invalid line: \"%s: ...\"\n", key); ++ continue; ++ } ++ *p = '\0'; ++ } ++ ++ // ++ // Remove last newline and last whitespace... ++ // ++ ++ dstrremovenewline(value); ++ ++ dstrtrim_right(value); ++ ++ // ++ // Skip empty values if there are any... ++ // ++ ++ if (!value->data || !value->data[0]) ++ continue; ++ ++ // ++ // Save data value ++ // ++ ++ if (!cupsArrayFind(data, value->data)) ++ cupsArrayAdd(data, value->data); ++ } ++ ++ free_dstr(value); ++} ++ ++ ++// ++// `get_values_from_ppd()` - Open the PPD file and get values of ++// desired FoomaticRIP PPD keywords... ++// ++ ++int // O - Return value, 0 - success, 1 - error ++get_values_from_ppd(cups_array_t *data, // O - Array of found FoomaticRIP* values ++ char *filename) // I - Path to the file ++{ ++ cups_file_t *file = NULL; // File descriptor ++ int ret = 0; // Return value ++ ++ if (!is_valid_path(filename, IS_FILE)) ++ return (1); ++ ++ if ((file = cupsFileOpen(filename, "r")) == NULL) ++ { ++ fprintf(stderr, "Cannot open \"%s\" for reading.\n", filename); ++ return (1); ++ } ++ ++ find_foomaticrip_keywords(data, file); ++ ++ cupsFileClose(file); ++ ++ return (ret); ++} ++ ++ ++#if defined(HAVE_LIBPPD) ++// ++// `copy_col()` - Allocation function for collection struct. ++// ++ ++ppd_collection_t * // O - Dynamically allocated PPD collection struct ++copy_col(char *path) // I - Directory with drivers ++{ ++ ppd_collection_t *col = NULL; ++ ++ if ((col = (ppd_collection_t*)calloc(1, sizeof(ppd_collection_t))) == NULL) ++ { ++ fprintf(stderr, "Cannot allocate memory for PPD collection.\n"); ++ return (NULL); ++ } ++ ++ if ((col->path = (char*)calloc(strlen(path) + 1, sizeof(char))) == NULL) ++ { ++ fprintf(stderr, "Cannot allocate memory for PPD path.\n"); ++ free(col); ++ return (NULL); ++ } ++ ++ snprintf(col->path, strlen(path) + 1, "%s", path); ++ ++ return (col); ++} ++ ++ ++// ++// `free_col()` - Free function for PPD collection. ++// ++ ++void ++free_col(ppd_collection_t *col) // I - PPD collection ++{ ++ free(col->path); ++ free(col); ++} ++ ++ ++// ++// `compare_col()` - Comparing function for PPD collection. ++// ++ ++int // O - Result of comparison, 0 - the same, 1 - differs ++compare_col(char *a, // I - PPD collection ++ ppd_collection_t *b) // I - PPD collection ++{ ++ if(!strcmp(a, b->path)) ++ return (0); ++ ++ return (1); ++} ++#endif // HAVE_LIBPPD ++ ++ ++// ++// `get_values_from_ppdpaths()` - Goes via sent list of directories, gets ++// PPDs and gets value strings for FoomaticRIP related PPD keywords. ++// ++ ++int // O - Return value, 0 - success, 1 - error ++get_values_from_ppdpaths(cups_array_t *data, // O - Array of found values ++ char *ppdpaths) // I - List of directories with drivers, comma separated ++{ ++#if defined(HAVE_LIBPPD) ++ char *path = NULL, // Directory path ++ *start = NULL, // Helper pointer to start of string ++ *end = NULL; // Helper pointer to end of string ++ cups_array_t *ppd_collections = NULL, // Directories with drivers ++ *ppds = NULL; // PPD URIs ++ cups_file_t *ppdfile = NULL; // PPD file descriptor ++ int ret = 0; // Return value ++ ppd_info_t *ppd = NULL; // In-memory record of PPD ++ ++ ++ if ((ppd_collections = cupsArrayNew3((cups_array_func_t)compare_col, NULL, NULL, 0, (cups_acopy_func_t)copy_col, (cups_afree_func_t)free_col)) == NULL) ++ { ++ fprintf(stderr, "Could not allocate PPD collection array.\n"); ++ return (1); ++ } ++ ++ // ++ // Go through input directory list, validate each record, ++ // and add them into array... ++ // ++ ++ if ((path = strchr(ppdpaths, ',')) == NULL) ++ { ++ if (is_valid_path(ppdpaths, IS_DIR)) ++ cupsArrayAdd(ppd_collections, ppdpaths); ++ } ++ else ++ { ++ for (start = end = ppdpaths; *end; start = end) ++ { ++ if ((end = strchr(start, ',')) != NULL) ++ *end++ = '\0'; ++ else ++ end = start + strlen(start); ++ ++ if (is_valid_path(start, IS_DIR) && !cupsArrayFind(ppd_collections, start)) ++ cupsArrayAdd(ppd_collections, start); ++ } ++ } ++ ++ // ++ // Get array of in-memory PPD records, later used for generating the PPDs themselves... ++ // ++ ++ if ((ppds = ppdCollectionListPPDs(ppd_collections, 0, 0, NULL, NULL, NULL)) == NULL) ++ goto end; ++ ++ // ++ // Go through in-memory PPD records, generate a PPD and search for FoomaticRIP* keywords... ++ // ++ ++ for (ppd = (ppd_info_t*)cupsArrayGetFirst(ppds); ppd; ppd = (ppd_info_t*)cupsArrayGetNext(ppds)) ++ { ++ if ((ppdfile = ppdCollectionGetPPD(ppd->record.name, ppd_collections, NULL, NULL)) == NULL) ++ continue; ++ ++ find_foomaticrip_keywords(data, ppdfile); ++ ++ cupsFileClose(ppdfile); ++ } ++ ++ ++end: ++ for (ppd = (ppd_info_t*)cupsArrayGetFirst(ppds); ppd; ppd = (ppd_info_t*)cupsArrayGetNext(ppds)) ++ free(ppd); ++ ++ cupsArrayDelete(ppds); ++ ++ cupsArrayDelete(ppd_collections); ++ ++ return (ret); ++#else ++ fprintf(stdout, "foomatic-hash is not compiled with LIBPPD support.\n"); ++ ++ return (1); ++#endif // HAVE_LIBPPD ++} ++ ++ ++void ++help() ++{ ++ printf("Usage:\n" ++ "foomatic-hash --ppd \n" ++ "foomatic-hash --ppd-paths \n" ++ "\n" ++ "Finds values of FoomaticRIPCommandLine, FoomaticRIPPDFCommandLine\n" ++ "and FoomaticRIPOptionSetting from the specified PPDs, appends them\n" ++ "into the specified scan output for review, and hashes the found values.\n" ++ "\n" ++ "--ppd - PPD file to read\n" ++ "--ppd-paths - Paths to look for PPDs, available only with libppd\n" ++ " - Found required values from drivers\n" ++ " - Output file with hashes\n"); ++} ++ ++ ++int ++main(int argc, ++ char** argv) ++{ ++ cups_array_t *data = NULL; // Found FoomaticRIP* PPD keyword values ++ int ret = 1; ++ ++ ++ if (argc != 5) ++ { ++ help(); ++ return (0); ++ } ++ ++ // ++ // End up early if we can't write into paths provided as arguments ++ // ++ ++ if (!is_valid_path(argv[3], IS_FILE) || ++ ((data = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free)) == NULL) || ++ !is_valid_path(argv[4], IS_FILE)) ++ return (1); ++ ++ // ++ // We scan single PPD file, or from directory (if libppd support is present) ++ // ++ ++ if (!strcmp(argv[1], "--ppd")) ++ { ++ if (get_values_from_ppd(data, argv[2])) ++ return (1); ++ } ++ else if (!strcmp(argv[1], "--ppd-paths")) ++ { ++ if (get_values_from_ppdpaths(data, argv[2])) ++ return (1); ++ } ++ else ++ { ++ fprintf(stderr, "Unsupported argument.\n"); ++ return (1); ++ } ++ ++ // ++ // Write found values of FoomaticRIPCommandLine, FoomaticRIPPDFCommandLine and FoomaticRIPOptionSetting ++ // PPD keywords... ++ // ++ ++ write_array(data, argv[3]); ++ ++ // ++ // Hash the found values.. ++ // ++ ++ ret = generate_hash_file(data, argv[4]); ++ ++ cupsArrayDelete(data); ++ ++ ++ return (ret); ++} +diff --git a/filter/foomatic-rip/foomaticrip.c b/filter/foomatic-rip/foomaticrip.c +index 036d6138b..2e892cb80 100644 +--- a/filter/foomatic-rip/foomaticrip.c ++++ b/filter/foomatic-rip/foomaticrip.c +@@ -35,72 +35,6 @@ + #include + + +-// Logging +-FILE* logh = NULL; +- +- +-void +-_logv(const char *msg, +- va_list ap) +-{ +- if (!logh) +- return; +- vfprintf(logh, msg, ap); +- fflush(logh); +-} +- +- +-void +-_log(const char* msg, +- ...) +-{ +- va_list ap; +- va_start(ap, msg); +- _logv(msg, ap); +- va_end(ap); +-} +- +- +-void +-close_log() +-{ +- if (logh && logh != stderr) +- fclose(logh); +-} +- +- +-int +-redirect_log_to_stderr() +-{ +- if (dup2(fileno(logh), fileno(stderr)) < 0) +- { +- _log("Could not dup logh to stderr\n"); +- return (0); +- } +- return (1); +-} +- +- +-void +-rip_die(int status, +- const char *msg, +- ...) +-{ +- va_list ap; +- +- _log("Process is dying with \""); +- va_start(ap, msg); +- _logv(msg, ap); +- va_end(ap); +- _log("\", exit stat %d\n", status); +- +- _log("Cleaning up...\n"); +- kill_all_processes(); +- +- exit(status); +-} +- +- + jobparams_t *job = NULL; + + +@@ -186,8 +120,6 @@ char cupsfilterpath[PATH_MAX] = "/usr/local/lib/cups/filter:" + "/opt/cups/filter:" + "/usr/lib/cups/filter"; + +-char modern_shell[] = SHELL; +- + + void + config_set_option(const char *key, +@@ -239,13 +171,6 @@ config_from_file(const char *filename) + } + + +-const char * +-get_modern_shell() +-{ +- return (modern_shell); +-} +- +- + // returns position in 'str' after the option + char * + extract_next_option(char *str, +diff --git a/filter/foomatic-rip/foomaticrip.h b/filter/foomatic-rip/foomaticrip.h +index 5c9ecfaf0..60d4059c6 100644 +--- a/filter/foomatic-rip/foomaticrip.h ++++ b/filter/foomatic-rip/foomaticrip.h +@@ -32,36 +32,6 @@ + #define LOG_FILE "/tmp/foomatic-rip" + #endif + +- +-// Constants used by this filter +-// +-// Error codes, as some spoolers behave different depending on the reason why +-// the RIP failed, we return an error code. +- +-#define EXIT_PRINTED 0 // file was printed normally +-#define EXIT_PRNERR 1 // printer error occured +-#define EXIT_PRNERR_NORETRY 2 // printer error with no hope +- // of retry +-#define EXIT_JOBERR 3 // job is defective +-#define EXIT_SIGNAL 4 // terminated after catching +- // signal +-#define EXIT_ENGAGED 5 // printer is otherwise engaged +- // (connection refused) +-#define EXIT_STARVED 6 // starved for system resources +-#define EXIT_PRNERR_NORETRY_ACCESS_DENIED 7 // bad password? bad port +- // permissions? +-#define EXIT_PRNERR_NOT_RESPONDING 8 // just doesn't answer at all +- // (turned off?) +-#define EXIT_PRNERR_NORETRY_BAD_SETTINGS 9 // interface settings are +- // invalid +-#define EXIT_PRNERR_NO_SUCH_ADDRESS 10 // address lookup failed, may +- // be transient +-#define EXIT_PRNERR_NORETRY_NO_SUCH_ADDRESS 11 // address lookup failed, not +- // transient +-#define EXIT_INCAPABLE 50 // printer wants (lacks) +- // features or resources +- +- + // Supported spoolers are currently: + // + // cups - CUPS - Common Unix Printing System +@@ -73,11 +43,6 @@ + // The spooler from which foomatic-rip was called. set in main() + extern int spooler; + +-#ifndef PATH_MAX +-#define PATH_MAX 4096 +-#endif +-#define CMDLINE_MAX 65536 +- + typedef struct + { + char printer[256]; +@@ -95,11 +60,6 @@ typedef struct + + jobparams_t *get_current_job(); + +-void _log(const char* msg, ...); +-int redirect_log_to_stderr(); +-void rip_die(int status, const char *msg, ...); +- +-const char *get_modern_shell(); + FILE *open_postpipe(); + + extern struct dstr *currentcmd; +diff --git a/filter/foomatic-rip/process.c b/filter/foomatic-rip/process.c +index 51bf57571..fc8e81cc7 100644 +--- a/filter/foomatic-rip/process.c ++++ b/filter/foomatic-rip/process.c +@@ -22,6 +22,8 @@ + + int kidgeneration = 0; + ++char modern_shell[] = SHELL; ++ + struct process + { + char name[64]; +@@ -39,6 +41,13 @@ struct process procs[MAX_CHILDS] = + }; + + ++const char * ++get_modern_shell() ++{ ++ return (modern_shell); ++} ++ ++ + void + add_process(const char *name, + int pid, +diff --git a/filter/foomatic-rip/process.h b/filter/foomatic-rip/process.h +index 54a42923a..db597bcef 100644 +--- a/filter/foomatic-rip/process.h ++++ b/filter/foomatic-rip/process.h +@@ -18,11 +18,14 @@ + #include + + ++extern char modern_shell[]; ++ + pid_t start_process(const char *name, int (*proc_func)(FILE*, FILE*, void*), void *user_arg, + FILE **fdin, FILE **fdout); + pid_t start_system_process(const char *name, const char *command, FILE **fdin, + FILE **fdout); + ++const char *get_modern_shell(); + // returns command's return status (see waitpid(2)) + int run_system_process(const char *name, const char *command); + +diff --git a/filter/foomatic-rip/util.c b/filter/foomatic-rip/util.c +index 78ca5212c..508bc0958 100644 +--- a/filter/foomatic-rip/util.c ++++ b/filter/foomatic-rip/util.c +@@ -11,7 +11,9 @@ + // + + #include "util.h" +-#include "foomaticrip.h" ++#include "process.h" ++#include ++#include + #include + #include + #include +@@ -20,7 +22,71 @@ + #include + + ++const char *hash_alg = "sha2-256"; // Used hash algorithm + const char *shellescapes = "|;<>&!$\'\"`#*?()[]{}"; ++FILE* logh = NULL; ++ ++// Logging ++void ++_logv(const char *msg, ++ va_list ap) ++{ ++ if (!logh) ++ return; ++ vfprintf(logh, msg, ap); ++ fflush(logh); ++} ++ ++ ++void ++_log(const char* msg, ++ ...) ++{ ++ va_list ap; ++ va_start(ap, msg); ++ _logv(msg, ap); ++ va_end(ap); ++} ++ ++ ++void ++close_log() ++{ ++ if (logh && logh != stderr) ++ fclose(logh); ++} ++ ++ ++int ++redirect_log_to_stderr() ++{ ++ if (dup2(fileno(logh), fileno(stderr)) < 0) ++ { ++ _log("Could not dup logh to stderr\n"); ++ return (0); ++ } ++ return (1); ++} ++ ++ ++void ++rip_die(int status, ++ const char *msg, ++ ...) ++{ ++ va_list ap; ++ ++ _log("Process is dying with \""); ++ va_start(ap, msg); ++ _logv(msg, ap); ++ va_end(ap); ++ _log("\", exit stat %d\n", status); ++ ++ _log("Cleaning up...\n"); ++ kill_all_processes(); ++ ++ exit(status); ++} + + + const char * +@@ -1449,3 +1515,276 @@ copy_file(FILE *dest, + + return (!ferror(src) && !ferror(dest)); + } ++ ++ ++// ++// 'hash_data()' - Hash presented data with CUPS API hash function. ++// ++ ++int // O - success 0/error 1 ++hash_data(unsigned char *data, // I - Data to hash ++ size_t datalen, // I - Length of data ++ char *hash_string, // O - Hexadecimal hashed string ++ size_t string_len) // I - Length of hexadecimal hashed string ++{ ++ unsigned char hash[32]; // Array for saving hash ++ ++ ++ if ((cupsHashData(hash_alg, data, datalen, hash, sizeof(hash))) == -1) ++ { ++ fprintf(stderr, "\"%s\" - Error when hashing\n", data); ++ return (1); ++ } ++ ++ if ((cupsHashString(hash, sizeof(hash), hash_string, string_len)) == NULL) ++ { ++ fprintf(stderr, "Error when encoding hash to hexadecimal\n"); ++ return (1); ++ } ++ ++ return (0); ++} ++ ++ ++// ++// 'load_system_hashes()' - Load hashes from system. ++// ++ ++int // O - success 0 / error 1 ++load_system_hashes(cups_array_t **hashes) // O - Array of existing hashes ++{ ++ char filename[1024]; // Absolute path to file ++ cups_dir_t *dir = NULL; // CUPS struct representing dir ++ cups_dentry_t *dent = NULL; // CUPS struct representing an object in directory ++ int i = 0; // Array index ++ ++ // ++ // System directories to load system hashes from (defined in Makefile.am) ++ // ++ // SYS_HASH_PATH - /usr/share/foomatic/hashes.d by default ++ // USR_HASH_PATH - /etc/foomatic/hashes.d by default ++ // ++ ++ const char *dirs[] = { ++ SYS_HASH_PATH, ++ USR_HASH_PATH, ++ NULL ++ }; ++ ++ if (!hashes) ++ return (1); ++ ++ if ((*hashes = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free)) == NULL) ++ { ++ fprintf(stderr, "Could not allocate array for hashes.\n"); ++ return (1); ++ } ++ ++ // ++ // Go through files in directories and load hashes... ++ // ++ ++ while (dirs[i] != NULL) ++ { ++ if ((dir = cupsDirOpen(dirs[i])) == NULL) ++ { ++ fprintf(stderr, "Could not open the directory \"%s\" - ignoring...\n", dirs[i++]); ++ continue; ++ } ++ ++ while ((dent = cupsDirRead(dir)) != NULL) ++ { ++ // Ignore any unsafe files - dirs, symlinks, hidden files, non-root writable files... ++ ++ if (!strncmp(dent->filename, "../", 3) || ++ dent->fileinfo.st_uid || ++ (dent->fileinfo.st_mode & S_IWGRP) || ++ (dent->fileinfo.st_mode & S_ISUID) || ++ (dent->fileinfo.st_mode & S_IWOTH)) ++ continue; ++ ++ snprintf(filename, sizeof(filename), "%s/%s", dirs[i], dent->filename); ++ ++ if (!is_valid_path(filename, IS_FILE)) ++ continue; ++ ++ if (load_array(hashes, filename)) ++ continue; ++ } ++ ++ cupsDirClose(dir); ++ ++ i++; ++ } ++ ++ return (0); ++} ++ ++ ++// ++// `load_array()` - Loads data from file into CUPS array... ++// ++ ++int // O - Return value, 0 - success, 1 - error ++load_array(cups_array_t **ar, // O - CUPS array to fill up - NULL/pointer - caller is responsible for freeing memory ++ char *filename) // I - Path to a file ++{ ++ char line[2048]; // Input array for line reading ++ cups_file_t *fp = NULL; // File with data ++ ++ ++ // ++ // Make sure the file is valid and the pointer is not NULL... ++ // ++ ++ if (!is_valid_path(filename, IS_FILE) || !ar) ++ return (1); ++ ++ memset(line, 0, sizeof(line)); ++ ++ if (!*ar) ++ { ++ if((*ar = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0, (cups_acopy_func_t)strdup, (cups_afree_func_t)free)) == NULL) ++ { ++ fprintf(stderr, "Cannot allocate array.\n"); ++ *ar = NULL; ++ return (1); ++ } ++ } ++ ++ // ++ // Has to be accessible, but it is possible the file does not exist ++ // and will be created in the end... ++ // ++ ++ if (access(filename, F_OK)) ++ { ++ // ++ // It is fine for the file has not existed yet - it will be created in the end... ++ // ++ ++ if (errno == ENOENT) ++ return (0); ++ else ++ { ++ fprintf(stderr, "File \"%s\" is not accessible.\n", filename); ++ return (1); ++ } ++ } ++ ++ // ++ // Read the file line by line... ++ // ++ ++ if ((fp = cupsFileOpen(filename, "r")) == NULL) ++ { ++ fprintf(stderr, "Cannot open file \"%s\" for read.\n", filename); ++ return (1); ++ } ++ ++ while (cupsFileGets(fp, line, sizeof(line))) ++ { ++ if (!cupsArrayFind(*ar, line)) ++ cupsArrayAdd(*ar, line); ++ ++ memset(line, 0, sizeof(line)); ++ } ++ ++ cupsFileClose(fp); ++ ++ return (0); ++} ++ ++ ++// ++// `is_valid_path()` - Checks whether the input path is valid ++// - correct length, file type, correct characters... ++// ++ ++int // O - Boolean value, 0 - invalid/1 - valid ++is_valid_path(char *path, // I - Path ++ enum filetype type) // I - Desired file type - file/dir ++{ ++ char *filename = NULL; // Filename stripped of possible path ++ struct stat fileinfo; // For checking whether file is symlink/dir ++ size_t len = strlen(path); // Path len ++ ++ // ++ // Check whether the whole path is not too long... ++ // ++ ++ if (len > PATH_MAX || len == 0) ++ return (0); ++ ++ // ++ // Be sure we can access the file, is of the correct filetype and is not symlink... ++ // Non-existing file is okay at the moment. ++ // ++ ++ if (stat(path, &fileinfo)) ++ { ++ if (errno != ENOENT) ++ { ++ fprintf(stderr, "The provided filename \"%s\" is not an acceptable file - %s.\n", path, strerror(errno)); ++ return (0); ++ } ++ } ++ else ++ { ++ if ((type & IS_FILE) && S_ISDIR(fileinfo.st_mode)) ++ { ++ fprintf(stderr, "The provided filename \"%s\" is not a file.\n", path); ++ return (0); ++ } ++ ++ if ((type & IS_DIR) && !S_ISDIR(fileinfo.st_mode)) ++ { ++ fprintf(stderr, "The provided filename \"%s\" is not a directory.\n", path); ++ return (0); ++ } ++ ++ if (S_ISLNK(fileinfo.st_mode)) ++ { ++ fprintf(stderr, "The provided filename \"%s\" is a symlink, which is not allowed.\n", path); ++ return (0); ++ } ++ } ++ ++ // ++ // We accept paths only with alphanumeric characters, dots, dashes, underscores, slashes... ++ // ++ ++ for (int i = 0; i < len - 1; i++) ++ { ++ if (!isalnum(path[i]) && path[i] != '.' && ++ path[i] != '-' && path[i] != '_' && ++ path[i] != '/') ++ { ++ fprintf(stderr, "The provided path contain non-ASCII characters.\n"); ++ return (0); ++ } ++ } ++ ++ // ++ // Get the filename itself... ++ // ++ ++ if ((filename = strrchr(path, '/')) == NULL) ++ filename = path; ++ else ++ filename++; ++ ++ if (strlen(filename) > NAME_MAX) ++ { ++ fprintf(stderr, "The filename is too long.\n"); ++ return (0); ++ } ++ ++ if (filename[0] == '.') ++ { ++ fprintf(stderr, "No hidden files.\n"); ++ return (0); ++ } ++ ++ return (1); ++} +diff --git a/filter/foomatic-rip/util.h b/filter/foomatic-rip/util.h +index e071e8151..ef275e411 100644 +--- a/filter/foomatic-rip/util.h ++++ b/filter/foomatic-rip/util.h +@@ -18,11 +18,70 @@ + #endif + + #include "config.h" ++#include + #include + #include + ++#if CUPS_VERSION_MAJOR <= 2 && CUPS_VERSION_MINOR < 5 ++# define cupsArrayGetFirst(ar) cupsArrayFirst(ar) ++# define cupsArrayGetNext(ar) cupsArrayNext(ar) ++#endif ++ ++// Constants used by this filter ++// ++// Error codes, as some spoolers behave different depending on the reason why ++// the RIP failed, we return an error code. ++ ++#define EXIT_PRINTED 0 // file was printed normally ++#define EXIT_PRNERR 1 // printer error occured ++#define EXIT_PRNERR_NORETRY 2 // printer error with no hope ++ // of retry ++#define EXIT_JOBERR 3 // job is defective ++#define EXIT_SIGNAL 4 // terminated after catching ++ // signal ++#define EXIT_ENGAGED 5 // printer is otherwise engaged ++ // (connection refused) ++#define EXIT_STARVED 6 // starved for system resources ++#define EXIT_PRNERR_NORETRY_ACCESS_DENIED 7 // bad password? bad port ++ // permissions? ++#define EXIT_PRNERR_NOT_RESPONDING 8 // just doesn't answer at all ++ // (turned off?) ++#define EXIT_PRNERR_NORETRY_BAD_SETTINGS 9 // interface settings are ++ // invalid ++#define EXIT_PRNERR_NO_SUCH_ADDRESS 10 // address lookup failed, may ++ // be transient ++#define EXIT_PRNERR_NORETRY_NO_SUCH_ADDRESS 11 // address lookup failed, not ++ // transient ++#define EXIT_PRNERR_NOTALLOWED 12 // the value is not allowed on the system ++#define EXIT_INCAPABLE 50 // printer wants (lacks) ++ // features or resources ++ ++#ifndef PATH_MAX ++#define PATH_MAX 4096 ++#endif ++ ++#define CMDLINE_MAX 65536 ++ ++#ifndef NAME_MAX ++#define NAME_MAX 255 ++#endif ++ + ++extern const char *hash_alg; + extern const char* shellescapes; ++extern FILE* logh; ++ ++// used for path validation - parameter --ppd supports files, --ppd-paths directories ++enum filetype { ++ IS_FILE, ++ IS_DIR ++}; ++ ++// logging and exiting... ++void _log(const char* msg, ...); ++void rip_die(int status, const char *msg, ...); ++int redirect_log_to_stderr(); ++void close_log(); + + int isempty(const char *string); + const char * temp_dir(); +@@ -132,6 +191,14 @@ int contains_command(const char *cmdline, const char *cmd); + int copy_file(FILE *dest, FILE *src, const char *alreadyread, + size_t alreadyread_len); + ++// File related functions with CUPS arrays ++int load_array(cups_array_t **ar, char *filename); ++int is_valid_path(char *path, enum filetype type); ++ ++// Hash functions ++int hash_data(unsigned char* data, size_t datalen, char *hash_string, size_t string_len); ++int load_system_hashes(cups_array_t **hashes); ++ + // Dynamic string + typedef struct dstr + { +-- +2.50.1 + diff --git a/0001-rastertopclx.c-Fix-infinite-loop-caused-by-crafted-f.patch b/0001-rastertopclx.c-Fix-infinite-loop-caused-by-crafted-f.patch new file mode 100644 index 0000000..b5a334c --- /dev/null +++ b/0001-rastertopclx.c-Fix-infinite-loop-caused-by-crafted-f.patch @@ -0,0 +1,79 @@ +From 0fe46c511e81062575b05936f804eb18c9f0a011 Mon Sep 17 00:00:00 2001 +From: Zdenek Dohnal +Date: Wed, 12 Nov 2025 15:47:24 +0100 +Subject: [PATCH] rastertopclx.c: Fix infinite loop caused by crafted file + +Infinite loop happened because of crafted input raster file, which led +into heap buffer overflow of `CompressBuf` array. + +Based on comments there should be always some `count` when compressing +the data, and processing of crafted file ended with offset and count +being 0. + +Fixes CVE-2025-64524 +--- + filter/rastertopclx.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/filter/rastertopclx.c b/filter/rastertopclx.c +index ded86f114..39cb378bf 100644 +--- a/filter/rastertopclx.c ++++ b/filter/rastertopclx.c +@@ -825,10 +825,10 @@ StartPage(cf_filter_data_t *data, // I - filter data + } + + if (header->cupsCompression) +- CompBuffer = malloc(DotBufferSize * 4); ++ CompBuffer = calloc(DotBufferSize * 4, sizeof(unsigned char)); + + if (header->cupsCompression >= 3) +- SeedBuffer = malloc(DotBufferSize); ++ SeedBuffer = calloc(DotBufferSize, sizeof(unsigned char)); + + SeedInvalid = 1; + +@@ -1159,6 +1159,13 @@ CompressData(unsigned char *line, // I - Data to compress + seed ++; + count ++; + } ++ ++ // ++ // Bail out if we don't have count to compress ++ // ++ ++ if (count == 0) ++ break; + } + + // +@@ -1252,6 +1259,13 @@ CompressData(unsigned char *line, // I - Data to compress + + count = line_ptr - start; + ++ // ++ // Bail out if we don't have count to compress ++ // ++ ++ if (count == 0) ++ break; ++ + #if 0 + fprintf(stderr, + "DEBUG: offset=%d, count=%d, comp_ptr=%p(%d of %d)...\n", +@@ -1424,6 +1438,13 @@ CompressData(unsigned char *line, // I - Data to compress + + count = (line_ptr - start) / 3; + ++ // ++ // Bail out if we don't have count to compress ++ // ++ ++ if (count == 0) ++ break; ++ + // + // Place mode 10 compression data in the buffer; each sequence + // starts with a command byte that looks like: +-- +2.51.1 + diff --git a/ci.fmf b/ci.fmf new file mode 100644 index 0000000..c5aa0e0 --- /dev/null +++ b/ci.fmf @@ -0,0 +1 @@ +resultsdb-testcase: separate diff --git a/cups-filters-1.28.13.tar.xz b/cups-filters-1.28.13.tar.xz new file mode 100644 index 0000000..d768975 Binary files /dev/null and b/cups-filters-1.28.13.tar.xz differ diff --git a/cups-filters-apremotequeueid.patch b/cups-filters-apremotequeueid.patch deleted file mode 100644 index 62deee5..0000000 --- a/cups-filters-apremotequeueid.patch +++ /dev/null @@ -1,42 +0,0 @@ -diff -up cups-filters-1.13.4/utils/cups-browsed.c.apremotequeueid cups-filters-1.13.4/utils/cups-browsed.c ---- cups-filters-1.13.4/utils/cups-browsed.c.apremotequeueid 2017-02-24 12:14:22.099865138 +0100 -+++ cups-filters-1.13.4/utils/cups-browsed.c 2017-02-24 12:18:44.039943820 +0100 -@@ -1,4 +1,4 @@ --/*** -+ /*** - This file is part of cups-filters. - - This file is free software; you can redistribute it and/or modify it -@@ -3491,7 +3491,8 @@ gboolean handle_cups_queues(gpointer unu - cups_job_t *jobs; - ipp_t *request; - time_t current_time = time(NULL); -- int i, new_cupsfilter_line_inserted, cont_line_read, want_raw; -+ int i, new_cupsfilter_line_inserted, ap_remote_queue_id_line_inserted, -+ cont_line_read, want_raw; - char *disabled_str, *ptr, *prefix; - const char *loadedppd = NULL; - int pass_through_ppd; -@@ -3813,6 +3814,7 @@ gboolean handle_cups_queues(gpointer unu - " and inhibiting client-side filtering of the job" : ""), - buf); - new_cupsfilter_line_inserted = 0; -+ ap_remote_queue_id_line_inserted = 0; - cont_line_read = 0; - while (cupsFileGets(in, line, sizeof(line))) { - if (pass_through_ppd == 1 && -@@ -3896,6 +3898,14 @@ gboolean handle_cups_queues(gpointer unu - cupsFilePrintf(out, "%s\n", line); - } else if (cont_line_read == 0 || strncmp(line, "*End", 4)) { - cont_line_read = 0; -+ /* Write an "APRemoteQueueID" line to make this queue marked -+ as remote printer by CUPS */ -+ if (strncmp(line, "*%", 2) && -+ strncmp(line, "*PPD-Adobe:", 11) && -+ ap_remote_queue_id_line_inserted == 0) { -+ ap_remote_queue_id_line_inserted = 1; -+ cupsFilePrintf(out, "*APRemoteQueueID: \"\"\n"); -+ } - /* Simply write out the line as we read it */ - cupsFilePrintf(out, "%s\n", line); - } diff --git a/cups-filters.spec b/cups-filters.spec index d615cd3..8d236ce 100644 --- a/cups-filters.spec +++ b/cups-filters.spec @@ -1,104 +1,90 @@ +%if 0%{?fedora} +%bcond_without mdns +%bcond_without braille +%else +%bcond_with mdns +%bcond_with braille +%endif + +# currently we use CUPS PPD compiler which will be removed +# in CUPS 3.0, then we will use PPD compiler from libppd-tools +%bcond_without cups_ppdc + # we build CUPS also with relro %global _hardened_build 1 -Summary: OpenPrinting CUPS filters and backends +Summary: OpenPrinting CUPS filters for CUPS 2.X Name: cups-filters -Version: 1.13.4 -Release: 3%{?dist} +Epoch: 1 +Version: 2.0.1 +Release: 12%{?dist} -# For a breakdown of the licensing, see COPYING file -# GPLv2: filters: commandto*, imagetoraster, pdftops, rasterto*, -# imagetopdf, pstopdf, texttopdf -# backends: parallel, serial -# GPLv2+: filters: gstopxl, textonly, texttops, imagetops, foomatic-rip -# GPLv3: filters: bannertopdf -# GPLv3+: filters: urftopdf, rastertopdf -# LGPLv2+: utils: cups-browsed -# MIT: filters: gstoraster, pdftoijs, pdftoopvp, pdftopdf, pdftoraster -License: GPLv2 and GPLv2+ and GPLv3 and GPLv3+ and LGPLv2+ and MIT +# the CUPS exception text is the same as LLVM exception, so using that name with +# agreement from legal team +# https://lists.fedoraproject.org/archives/list/legal@lists.fedoraproject.org/message/A7GFSD6M3GYGSI32L2FC5KB22DUAEQI3/ +License: Apache-2.0 WITH LLVM-exception -Url: http://www.linuxfoundation.org/collaborate/workgroups/openprinting/cups-filters -Source0: http://www.openprinting.org/download/cups-filters/cups-filters-%{version}.tar.xz +URL: https://github.com/OpenPrinting/cups-filters +Source0: %{URL}/releases/download/%{version}/%{name}-%{version}.tar.gz +Source1: lftocrlf.ppd +Source2: lftocrlf -Patch01: cups-filters-apremotequeueid.patch -Requires: cups-filters-libs%{?_isa} = %{version}-%{release} +# Patches +# https://github.com/OpenPrinting/cups-filters/pull/618 +Patch001: 0001-Fix-build-failure-with-GCC-15-and-std-c23.patch +# introducing foomatic-hash, but without rejecting values in foomatic-rip +# https://github.com/OpenPrinting/cups-filters/pull/648 +Patch002: 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 +Patch003: foomatic-ripdie-error.patch +# rejecting the unknown values in foomatic-rip +# https://github.com/OpenPrinting/cups-filters/pull/648 +Patch004: foomaticrip-reject-unknown-values.patch +# CVE-2025-64524 fix +Patch005: 0001-rastertopclx.c-Fix-infinite-loop-caused-by-crafted-f.patch -# Obsolete cups-php (bug #971741) -Obsoletes: cups-php < 1:1.6.0-1 -# Don't Provide it because we don't build the php module -#Provides: cups-php = 1:1.6.0-1 -BuildRequires: cups-devel -BuildRequires: pkgconfig -# pdftopdf -BuildRequires: pkgconfig(libqpdf) -# pdftops -BuildRequires: poppler-utils -# pdftoijs, pdftoopvp, pdftoraster, gstoraster -BuildRequires: pkgconfig(poppler) -BuildRequires: poppler-cpp-devel -BuildRequires: libjpeg-devel -BuildRequires: libtiff-devel -BuildRequires: pkgconfig(libpng) -BuildRequires: pkgconfig(zlib) -BuildRequires: pkgconfig(dbus-1) -# libijs -BuildRequires: pkgconfig(ijs) -BuildRequires: pkgconfig(freetype2) -BuildRequires: pkgconfig(fontconfig) -BuildRequires: pkgconfig(lcms2) -# cups-browsed -BuildRequires: avahi-devel -BuildRequires: pkgconfig(avahi-glib) -BuildRequires: pkgconfig(glib-2.0) -BuildRequires: systemd - -# Make sure we get postscriptdriver tags. -BuildRequires: python-cups - -# Testing font for test scripts. -BuildRequires: dejavu-sans-fonts +# driverless backend/driver was moved into a separate package to +# remove avahi dependency for filters +# remove once C10S is released and F40 is EOL +Conflicts: cups-filters-driverless < 1:2.0.0-3 # autogen.sh BuildRequires: autoconf +# autogen.sh BuildRequires: automake +# filter binaries and backends are written in C +BuildRequires: gcc +# autogen.sh +BuildRequires: gettext-devel +# for autosetup +BuildRequires: git-core +# autogen.sh BuildRequires: libtool +# uses make for compiling +BuildRequires: make +# we use pkgconfig to get a proper devel packages +# proper CFLAGS and LDFLAGS +BuildRequires: pkgconf-pkg-config +# uses CUPS API +BuildRequires: pkgconfig(cups) >= 2.2.2 +# uses cupsfilters API +BuildRequires: pkgconfig(libcupsfilters) >= 2.0b3 +# uses PPD API +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 +%endif +# needs cups dirs Requires: cups-filesystem -Requires: poppler-utils -# texttopdf -Requires: liberation-mono-fonts - -# pstopdf -Requires: bc grep sed - -# cups-browsed -Requires(post): systemd -Requires(preun): systemd -Requires(postun): systemd - -# Ghostscript CUPS filters live here since Ghostscript 9.08. -Provides: ghostscript-cups = 9.08 -Obsoletes: ghostscript-cups < 9.08 - -# foomatic-rip's upstream moved from foomatic-filters to cups-filters-1.0.42 -Provides: foomatic-filters = 4.0.9-8 -Obsoletes: foomatic-filters < 4.0.9-8 - -%package libs -Summary: OpenPrinting CUPS filters and backends - cupsfilters and fontembed libraries -Group: System Environment/Libraries -# LGPLv2: libcupsfilters -# MIT: libfontembed -License: LGPLv2 and MIT - -%package devel -Summary: OpenPrinting CUPS filters and backends - development environment -Group: Development/Libraries -License: LGPLv2 and MIT -Requires: cups-filters-libs%{?_isa} = %{version}-%{release} %description Contains backends, filters, and other software that was @@ -107,184 +93,782 @@ Apple Inc. In addition it contains additional filters developed independently of Apple, especially filters for the PDF-centric printing workflow introduced by OpenPrinting. -%description libs -This package provides cupsfilters and fontembed libraries. -%description devel -This is the development package for OpenPrinting CUPS filters and backends. +%package driverless +Summary: OpenPrinting driverless backends and drivers for CUPS 2.X +License: Apache-2.0 WITH LLVM-exception + +# backends and drivers has been moved from the main package to subpackage +# to remove the avahi/mdns dependency needed for driverless +# remove after F40 is EOL and C10S is released +Conflicts: cups-filters < 1:2.0.0-3 + +# finding device via driverless depends on running avahi-daemon +Requires: avahi +# ippfind is used in driverless backend, not needed classic PPD based print queue +Requires: cups-ipptool +# cups-browsed needs systemd-resolved or nss-mdns for resolving .local addresses of remote print queues +# let's not require a specific package and let the user decide what he wants to use. +# just recommend nss-mdns for Fedora for now to have working default, but +# don't hardwire it for resolved users +%if %{with mdns} +Recommends: nss-mdns +%endif + +# needs cups dirs +Requires: cups-filesystem + + +%description driverless +Contains backends and drivers for driverless implementation for cups-filters, +which makes driverless printers to be seen when listing printers nearby and gives +a specific generated driver for driverless printer in the local network. They are +tools for backward compatibility with applications which don't handle CUPS temporary +queues. + %prep -%setup -q -%patch01 -p1 -b .apremotequeueid +%autosetup -S git -N + +%if 0%{?fedora} >= 43 || 0%{?rhel} >=9 +%autopatch +%else +%autopatch -M 3 +%endif + %build # work-around Rpath ./autogen.sh -# --with-pdftops=hybrid - use Poppler's pdftops instead of Ghostscript for -# Brother, Minolta, and Konica Minolta to work around -# bugs in the printer's PS interpreters -# --with-rcdir=no - don't install SysV init script -%configure --disable-static \ +%configure --enable-driverless \ + --enable-individual-cups-filters \ + --disable-universal-cups-filter \ + --disable-mutool \ + --disable-rpath \ --disable-silent-rules \ - --with-pdftops=hybrid \ - --enable-dbus \ - --with-rcdir=no \ - --disable-mutool + --disable-static + +%make_build -make %{?_smp_mflags} %install -make install DESTDIR=%{buildroot} +%make_install -# Don't ship libtool la files. -rm -f %{buildroot}%{_libdir}/lib*.la +# 2229776 - Add textonly driver back, but as lftocrlf +install -p -m 0755 %{SOURCE2} %{buildroot}%{_cups_serverbin}/filter/lftocrlf +install -p -m 0644 %{SOURCE1} %{buildroot}%{_datadir}/ppd/cupsfilters/lftocrlf.ppd -# Not sure what is this good for. -rm -f %{buildroot}%{_bindir}/ttfread +# remove this once F43 is EOL +%if 0%{?fedora} >= 43 || 0%{?rhel} >=9 -rm -f %{buildroot}%{_pkgdocdir}/INSTALL -mkdir -p %{buildroot}%{_pkgdocdir}/fontembed/ -cp -p fontembed/README %{buildroot}%{_pkgdocdir}/fontembed/ +mkdir -p %{buildroot}%{_libexecdir}/%{name} + +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 -# systemd unit file mkdir -p %{buildroot}%{_unitdir} -install -p -m 644 utils/cups-browsed.service %{buildroot}%{_unitdir} + +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 + +cat > %{buildroot}%{_unitdir}/cups.service.d/10-foomaticrip-upgrade.conf << EOF +[Unit] +After=foomaticrip-upgrade.service +Wants=foomaticrip-upgrade.service +EOF + +%endif + # 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 +# among distibutions, so rather have the strict foomatic-rip filter... ln -sf %{_cups_serverbin}/filter/foomatic-rip %{buildroot}%{_bindir}/foomatic-rip -# imagetobrf is going to be mapped as /usr/lib/cups/filter/imagetoubrl -ln -sf imagetobrf %{buildroot}%{_cups_serverbin}/filter/imagetoubrl +%if %{with cups_ppdc} +mkdir -p %{buildroot}%{_datadir}/cups/ppdc +mv %{buildroot}%{_datadir}/{ppdc/pcl.h,cups/ppdc/pcl.h} +mv %{buildroot}%{_datadir}/{ppdc/escp.h,cups/ppdc/escp.h} +%endif -# textbrftoindex3 is going to be mapped as /usr/lib/cups/filter/textbrftoindexv4 -ln -sf textbrftoindexv3 %{buildroot}%{_cups_serverbin}/filter/textbrftoindexv4 +# remove license files which are in %%pkgdocdir +rm -f %{buildroot}%{_pkgdocdir}/{COPYING,NOTICE,LICENSE} -# Don't ship urftopdf for now (bug #1002947). -rm -f %{buildroot}%{_cups_serverbin}/filter/urftopdf -sed -i '/urftopdf/d' %{buildroot}%{_datadir}/cups/mime/cupsfilters.convs +# remove INSTALL since it is unnecessary +rm -f %{buildroot}%{_pkgdocdir}/INSTALL -# Don't ship pdftoopvp for now (bug #1027557). -rm -f %{buildroot}%{_cups_serverbin}/filter/pdftoopvp -rm -f %{buildroot}%{_sysconfdir}/fonts/conf.d/99pdftoopvp.conf +# remove CHANGES-1.x.md, since it is carried by a dependency +rm -f %{buildroot}%{_pkgdocdir}/CHANGES-1.x.md %check make check + %post -%systemd_post cups-browsed.service - -# Initial installation -if [ $1 -eq 1 ] ; then - IN=%{_sysconfdir}/cups/cupsd.conf - OUT=%{_sysconfdir}/cups/cups-browsed.conf - keyword=BrowsePoll - - # We can remove this after few releases, it's just for the introduction of cups-browsed. - if [ -f "$OUT" ]; then - echo -e "\n# NOTE: This file is not part of CUPS.\n# You need to enable cups-browsed service\n# and allow ipp-client service in firewall." >> "$OUT" - fi - - # move BrowsePoll from cupsd.conf to cups-browsed.conf - if [ -f "$IN" ] && grep -iq ^$keyword "$IN"; then - if ! grep -iq ^$keyword "$OUT"; then - (cat >> "$OUT" <> "$OUT") || : - #systemctl enable cups-browsed.service >/dev/null 2>&1 || : - fi - sed -i -e "s,^$keyword,#$keyword directive moved to cups-browsed.conf\n#$keyword,i" "$IN" || : - fi +# remove PPD cache to make bz#2351389 fix work right away +# remove after F43 EOL +if [ $1 -gt 1 ] +then + rm -f /var/cache/cups/ppds.dat || : fi +%if 0%{?fedora} >= 43 || 0%{?rhel} >=9 + %systemd_post foomaticrip-upgrade.service +%endif + + %preun -%systemd_preun cups-browsed.service +%if 0%{?fedora} >= 43 || 0%{?rhel} >=9 + %systemd_preun foomaticrip-upgrade.service +%endif + %postun -%systemd_postun_with_restart cups-browsed.service +%if 0%{?fedora} >= 43 || 0%{?rhel} >=9 + %systemd_postun foomaticrip-upgrade.service +%endif -%post libs -p /sbin/ldconfig -%postun libs -p /sbin/ldconfig +%posttrans +%if 0%{?fedora} >= 43 || 0%{?rhel} >=9 + %systemd_posttrans_with_reload foomaticrip-upgrade.service +%endif + +if [ $1 -gt 1 ] +then + # since we moved to individual filters, we have to restart cups + # to load new conversion tables if it is running + # remove by F43 EOL and C11S release + if systemctl is-active cups &> /dev/null + then + systemctl restart cups || : + fi + + %if 0%{?fedora} >= 43 || 0%{?rhel} >=9 + systemctl start foomaticrip-upgrade.service || : + %endif +fi %files -%{_pkgdocdir}/README -%{_pkgdocdir}/AUTHORS -%{_pkgdocdir}/NEWS -%config(noreplace) %{_sysconfdir}/cups/cups-browsed.conf -%attr(0755,root,root) %{_cups_serverbin}/filter/* -%attr(0755,root,root) %{_cups_serverbin}/backend/parallel -# Serial backend needs to run as root (bug #212577#c4). -%attr(0700,root,root) %{_cups_serverbin}/backend/serial -%attr(0755,root,root) %{_cups_serverbin}/backend/implicitclass -%attr(0755,root,root) %{_cups_serverbin}/backend/beh +%license COPYING LICENSE NOTICE +%doc AUTHORS ABOUT-NLS CHANGES.md CONTRIBUTING.md DEVELOPING.md README.md +%{_bindir}/foomatic-hash %{_bindir}/foomatic-rip -%{_bindir}/driverless -%{_cups_serverbin}/backend/driverless -%{_cups_serverbin}/driver/driverless -%{_datadir}/cups/banners -%{_datadir}/cups/braille -%{_datadir}/cups/charsets -%{_datadir}/cups/data/* -# this needs to be in the main package because of cupsfilters.drv -%{_datadir}/cups/ppdc/pcl.h -%{_datadir}/cups/ppdc/braille.defs -%{_datadir}/cups/ppdc/fr-braille.po -%{_datadir}/cups/ppdc/imagemagick.defs -%{_datadir}/cups/ppdc/index.defs -%{_datadir}/cups/ppdc/liblouis.defs -%{_datadir}/cups/ppdc/liblouis1.defs -%{_datadir}/cups/ppdc/liblouis2.defs -%{_datadir}/cups/ppdc/liblouis3.defs -%{_datadir}/cups/ppdc/liblouis4.defs -%{_datadir}/cups/ppdc/media-braille.defs +%attr(0744,root,root) %{_cups_serverbin}/backend/beh +# all backends needs to be run only as root because of kerberos +%attr(0744,root,root) %{_cups_serverbin}/backend/parallel +# Serial backend needs to run as root (bug #212577#c4). +%attr(0744,root,root) %{_cups_serverbin}/backend/serial +%attr(0755,root,root) %{_cups_serverbin}/filter/bannertopdf +%attr(0755,root,root) %{_cups_serverbin}/filter/commandtoescpx +%attr(0755,root,root) %{_cups_serverbin}/filter/commandtopclx +%attr(0755,root,root) %{_cups_serverbin}/filter/foomatic-rip +%attr(0755,root,root) %{_cups_serverbin}/filter/gstopdf +%attr(0755,root,root) %{_cups_serverbin}/filter/gstopxl +%attr(0755,root,root) %{_cups_serverbin}/filter/gstoraster +%attr(0755,root,root) %{_cups_serverbin}/filter/imagetopdf +%attr(0755,root,root) %{_cups_serverbin}/filter/imagetops +%attr(0755,root,root) %{_cups_serverbin}/filter/imagetoraster +# 2229776 - Add textonly driver back, but as lftocrlf +%attr(0755,root,root) %{_cups_serverbin}/filter/lftocrlf +%attr(0755,root,root) %{_cups_serverbin}/filter/pclmtoraster +%attr(0755,root,root) %{_cups_serverbin}/filter/pdftopdf +%attr(0755,root,root) %{_cups_serverbin}/filter/pdftops +%attr(0755,root,root) %{_cups_serverbin}/filter/pdftoraster +%attr(0755,root,root) %{_cups_serverbin}/filter/pwgtopclm +%attr(0755,root,root) %{_cups_serverbin}/filter/pwgtopdf +%attr(0755,root,root) %{_cups_serverbin}/filter/pwgtoraster +%attr(0755,root,root) %{_cups_serverbin}/filter/rastertoescpx +%attr(0755,root,root) %{_cups_serverbin}/filter/rastertopclx +%attr(0755,root,root) %{_cups_serverbin}/filter/rastertops +%attr(0755,root,root) %{_cups_serverbin}/filter/texttopdf +%attr(0755,root,root) %{_cups_serverbin}/filter/texttops +%attr(0755,root,root) %{_cups_serverbin}/filter/texttotext %{_datadir}/cups/drv/cupsfilters.drv -%{_datadir}/cups/drv/generic-brf.drv -%{_datadir}/cups/drv/indexv3.drv -%{_datadir}/cups/drv/indexv4.drv %{_datadir}/cups/mime/cupsfilters.types %{_datadir}/cups/mime/cupsfilters.convs %{_datadir}/cups/mime/cupsfilters-ghostscript.convs +%{_datadir}/cups/mime/cupsfilters-individual.convs %{_datadir}/cups/mime/cupsfilters-poppler.convs -%{_datadir}/cups/mime/braille.convs -%{_datadir}/cups/mime/braille.types +%dir %{_datadir}/foomatic +%dir %{_datadir}/foomatic/hashes.d %{_datadir}/ppd/cupsfilters -%{_sbindir}/cups-browsed -%{_unitdir}/cups-browsed.service -%{_mandir}/man8/cups-browsed.8.gz -%{_mandir}/man5/cups-browsed.conf.5.gz +%if %{with cups_ppdc} +# escp.h and pcl.h are required during runtime, because +# CUPS PPD compiler (ppdc) uses them for generating drivers +# per request from cupsfilters.drv file +%{_datadir}/cups/ppdc/escp.h +%{_datadir}/cups/ppdc/pcl.h +%else +%dir %{_datadir}/ppdc +%{_datadir}/ppdc/escp.h +%{_datadir}/ppdc/pcl.h +%endif +%{_mandir}/man1/foomatic-hash.1.gz %{_mandir}/man1/foomatic-rip.1.gz +%config(noreplace) %{_sysconfdir}/foomatic +%if 0%{?fedora} >= 43 || 0%{?rhel} >=9 +%dir %{_libexecdir}/%{name} +%attr(0744,root,root) %{_libexecdir}/%{name}/posttrans.sh +%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 +%endif + +%files driverless +%license COPYING LICENSE NOTICE +%{_bindir}/driverless +%{_bindir}/driverless-fax +%{_cups_serverbin}/backend/driverless +%{_cups_serverbin}/backend/driverless-fax +%{_cups_serverbin}/driver/driverless +%{_cups_serverbin}/driver/driverless-fax %{_mandir}/man1/driverless.1.gz -%files libs -%dir %{_pkgdocdir}/ -%{_pkgdocdir}/COPYING -%{_pkgdocdir}/fontembed/README -%{_libdir}/libcupsfilters.so.* -%{_libdir}/libfontembed.so.* - -%files devel -%{_includedir}/cupsfilters -%{_includedir}/fontembed -%{_datadir}/cups/ppdc/escp.h -%{_libdir}/pkgconfig/libcupsfilters.pc -%{_libdir}/pkgconfig/libfontembed.pc -%{_libdir}/libcupsfilters.so -%{_libdir}/libfontembed.so %changelog -* Thu Aug 10 2017 Zdenek Dohnal - 1.13.4-3 +* Fri Nov 28 2025 Zdenek Dohnal - 1:2.0.1-12 +- fix CVE-2025-64524 + +* Mon Nov 10 2025 Zdenek Dohnal - 1:2.0.1-11 +- change return value of foomatic-hash if built without libppd + +* Wed Oct 01 2025 Zdenek Dohnal - 1:2.0.1-10 +- protect older Fedoras from F43+ changes, fix installability report about hashes.new + +* Thu Jul 31 2025 Zdenek Dohnal - 1:2.0.1-9 +- Reject unknown values in foomatic-rip in F43+ + +* Wed Jul 30 2025 Zdenek Dohnal - 1:2.0.1-8 +- Introduce foomatic-hash, but not rejecting values in foomatic-rip + +* Wed Jul 23 2025 Fedora Release Engineering - 1:2.0.1-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + +* Mon Jun 09 2025 Zdenek Dohnal - 1:2.0.1-6 +- CUPS restart has to happen after universal filter is gone for good (in posttrans) (fedora#2370978) + +* Mon Jun 02 2025 Zdenek Dohnal - 1:2.0.1-5 +- individual filters have to explicitly enabled + +* Mon Jun 02 2025 Zdenek Dohnal - 1:2.0.1-4 +- disable universal filter for now - some 3rd party drivers did not work with it + +* Tue Mar 11 2025 Zdenek Dohnal - 1:2.0.1-3 +- textonly driver was missing (fedora#2351389) + +* Fri Jan 24 2025 Zdenek Dohnal - 1:2.0.1-2 +- fix FTBFS (fedora#2340017) + +* Thu Jan 16 2025 Fedora Release Engineering - 1:2.0.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild + +* Thu Aug 15 2024 Zdenek Dohnal - 1:2.0.1-1 +- 2.0.1 + +* Fri Jul 19 2024 Zdenek Dohnal - 1:2.0.0-9 +- fix missing epochs in conflicts + +* Wed Jul 17 2024 Fedora Release Engineering - 1:2.0.0-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + +* Tue May 28 2024 Zdenek Dohnal - 1:2.0.0-7 +- 2283295 - The directory /usr/share/ppdc/ is not in the RPM database. + +* Wed Jan 24 2024 Fedora Release Engineering - 1:2.0.0-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Fri Jan 19 2024 Fedora Release Engineering - 1:2.0.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Tue Dec 19 2023 Zdenek Dohnal - 1:2.0.0-4 +- make driverless subpackage require avahi and ipptool - they don't + work without them + +* Tue Dec 19 2023 Zdenek Dohnal - 1:2.0.0-3 +- introduce cups-filters-driverless to strip avahi dependency for filters + +* Tue Dec 19 2023 Zdenek Dohnal - 1:2.0.0-2 +- use exact foomatic-rip filter to comply with LSB + +* Thu Oct 19 2023 Zdenek Dohnal - 1:2.0.0-1 +- rebase to 2.0.0 + +* Mon Aug 07 2023 Zdenek Dohnal - 1:2.0~rc2-3 +- 2229776 - Add textonly driver back as lftocrlf driver + +* Wed Jul 19 2023 Fedora Release Engineering - 1:2.0~rc2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Wed Jun 28 2023 Zdenek Dohnal - 1:2.0~rc2-1 +- 2.0rc2 + +* Wed May 17 2023 Zdenek Dohnal - 1:2.0~rc1-2 +- 2207970 - CVE-2023-24805 cups-filters: remote code execution in cups-filters, beh CUPS backend + +* Thu Apr 27 2023 Zdenek Dohnal - 1:2.0~rc1-1 +- 2.0rc1 + +* Wed Mar 01 2023 Zdenek Dohnal - 1:2.0~b3-2 +- use epoch to ensure clean upgrade path, because I didn't read FPG carefully + +* Mon Feb 20 2023 Zdenek Dohnal - 2.0b3-1 +- 2170538 - rebase to 2.0b3 + +* Thu Jan 19 2023 Fedora Release Engineering - 1.28.16-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Thu Oct 13 2022 Zdenek Dohnal - 1.28.16-6 +- really build with qpdf-11.1.1 (forgot to wait for qpdf in side tag...) + +* Thu Oct 13 2022 Zdenek Dohnal - 1.28.16-5 +- rebuilt with qpdf-11.1.1 + +* Thu Sep 22 2022 Zdenek Dohnal - 1.28.16-4 +- rebuilt with qpdf-11.1.0 + +* Thu Sep 22 2022 Zdenek Dohnal - 1.28.16-3 +- build braille subpackage only on Fedora and CentOS Stream > 9 + +* Wed Sep 21 2022 Zdenek Dohnal - 1.28.16-2 +- disable frequent network interface data update, which slows down the queue creation + +* Thu Sep 08 2022 Zdenek Dohnal - 1.28.16-1 +- 1.28.16 + +* Thu Sep 08 2022 Zdenek Dohnal - 1.28.15-3 +- 2123809 - rpm -Va reports error on /etc/cups/cups-browsed.conf + +* Wed Jul 20 2022 Fedora Release Engineering - 1.28.15-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Wed Apr 20 2022 Zdenek Dohnal - 1.28.15-1 +- 1.28.15 + +* Thu Apr 07 2022 Zdenek Dohnal - 1.28.14-1 +- 1.28.14 + +* Mon Mar 28 2022 Zdenek Dohnal - 1.28.13-1 +- 1.28.13 + +* Tue Mar 08 2022 Zdenek Dohnal - 1.28.12-1 +- 1.28.12 + +* Thu Jan 20 2022 Fedora Release Engineering - 1.28.11-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Tue Jan 18 2022 Zdenek Dohnal - 1.28.11-2 +- raise the NVR to get a new build + +* Mon Jan 17 2022 Zdenek Dohnal - 1.28.11-1 +- 1.28.11 + +* Mon Jan 17 2022 Zdenek Dohnal - 1.28.10-4 +- fix typo in braille requires + +* Mon Jan 17 2022 Zdenek Dohnal - 1.28.10-3 +- 2040973 - Make Braille printing support optional + +* Mon Dec 06 2021 Zdenek Dohnal - 1.28.10-2 +- 1995728 - Enable braille printing + +* Tue Sep 14 2021 Zdenek Dohnal - 1.28.10-1 +- 1.28.10 + +* Tue Jul 27 2021 Zdenek Dohnal - 1.28.9-5 +- rebuilt with poppler-21.07.0 + +* Tue Jul 27 2021 Zdenek Dohnal - 1.28.9-4 +- remove build requirement on poppler-devel - we need just poppler-cpp-devel + +* Wed Jul 21 2021 Fedora Release Engineering - 1.28.9-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Wed Jul 14 2021 Zdenek Dohnal - 1.28.9-2 +- 1981603 - pdftopdf doesn't handle "page-range=10-2147483647" correctly + +* Mon Jun 21 2021 Zdenek Dohnal - 1.28.9-1 +- 1.28.9 + +* Mon Jun 21 2021 Zdenek Dohnal - 1.28.8-2 +- 1973056 - cups-browsed doesn't renew DBus subscription in time and all printing comes to a halt + +* Fri May 14 2021 Zdenek Dohnal - 1.28.8-1 +- 1.28.8 + +* Wed Apr 28 2021 Zdenek Dohnal - 1.28.7-7 +- 1954524 - cups-browsed doesn't save "*-default" options + +* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek - 1.28.7-6 +- Rebuilt for updated systemd-rpm-macros + See https://pagure.io/fesco/issue/2583. + +* Mon Feb 01 2021 Zdenek Dohnal - 1.28.7-5 +- put nss-mdns only for Fedora + +* Thu Jan 28 2021 Zdenek Dohnal - 1.28.7-4 +- remove nss-mdns - dont require a specific way how to resolve .local addresses + +* Tue Jan 26 2021 Fedora Release Engineering - 1.28.7-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Mon Jan 25 2021 Zdenek Dohnal - 1.28.7-2 +- unpush fix for 1904405 - M281fdw now often chokes on URF + +* Mon Jan 11 2021 Zdenek Dohnal - 1.28.7-1 +- 1.28.7, urftopdf nor pdftoopvp aren't compiled anymore +- 1904405 - HP M281fdw: čžš characters printed as squares with "driverless" driver + +* Mon Dec 07 2020 Zdenek Dohnal - 1.28.6-1 +- 1.28.6 + +* Tue Dec 01 2020 Zdenek Dohnal - 1.28.5-4 +- filters using ijs were removed, removed the dep + +* Tue Nov 24 2020 Zdenek Dohnal - 1.28.5-3 +- fix various memory issues within cups-browsed + +* Thu Nov 05 2020 Zdenek Dohnal - 1.28.5-2 +- use make and git-core + +* Mon Nov 02 2020 Zdenek Dohnal - 1.28.5-1 +- 1.28.5, 1881365 - cups-browsed crashing + +* Tue Sep 29 2020 Zdenek Dohnal - 1.28.2-3 +- 1891720 - foomatic-rip files up /var/spool/tmp with temporary files + +* Thu Sep 17 2020 Zdenek Dohnal - 1.28.2-2 +- 1879147 - driverless cannot generate ppd for dns-sd based uris + +* Tue Sep 15 2020 Zdenek Dohnal - 1.28.2-1 +- 1.28.2 + +* Thu Sep 03 2020 Zdenek Dohnal - 1.28.1-2 +- revert previous commit - systemd-resolved doesn't work with avahi right now + because missing link in NetworkManager + +* Mon Aug 31 2020 Zdenek Dohnal - 1.28.1-2 +- MDNS resolving should be done by systemd-resolved now + +* Thu Aug 27 2020 Zdenek Dohnal - 1.28.1-1 +- 1.28.1 - added driverless fax support + +* Fri Aug 21 2020 Zdenek Dohnal - 1.27.5-7 +- use configure option instead of downstream, cups-browsed.conf editing, patch +- the exact path in cups-browsed manpage was removed, use the patch removing it instead of downstream one +- use configure option to dont save queues between restarts instead of downstream patch reverting the issue +- memory leaks patch is from upstream too + +* Wed Aug 19 2020 Zdenek Dohnal - 1.27.5-6 +- 1867412 - cups-browsed leaks memory + +* Thu Aug 06 2020 Zdenek Dohnal - 1.27.5-5 +- require ipptool explicitly +- remove buildrequire on ipptool + +* Wed Aug 05 2020 Zdenek Dohnal - 1.27.5-4 +- use %%make_build and %%make_install according FPG +- own 'new' directories + +* Mon Jul 27 2020 Fedora Release Engineering - 1.27.5-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jul 20 2020 Zdenek Dohnal - 1.27.5-2 +- 1848575 - [cups, cups-filters] PPD generators creates invalid cupsManualCopies entry + +* Mon Jun 08 2020 Zdenek Dohnal - 1.27.5-1 +- 1.27.5 + +* Tue Apr 14 2020 Zdenek Dohnal - 1.27.4-1 +- 1.27.4 + +* Wed Apr 08 2020 Zdenek Dohnal - 1.27.3-3 +- memory issues in cups-browsed + +* Mon Apr 06 2020 Zdenek Dohnal - 1.27.3-2 +- make nss-mdns and avahi recommended + +* Mon Mar 23 2020 Zdenek Dohnal - 1.27.3-1 +- 1.27.3 + +* Fri Mar 13 2020 Zdenek Dohnal - 1.27.2-2 +- fix leaks in cups-browsed +- add require on nss-mdns + +* Mon Mar 02 2020 Zdenek Dohnal - 1.27.2-1 +- 1.27.2 + +* Tue Feb 25 2020 Zdenek Dohnal - 1.27.1-2 +- 1806862 - foomatic-rip handles empty files in bad way + +* Tue Feb 18 2020 Zdenek Dohnal - 1.27.1-1 +- 1.27.1 + +* Tue Feb 18 2020 Zdenek Dohnal - 1.27.0-2 +- 1802969 - Service "cups-browsed" is crashing all the time + +* Tue Jan 28 2020 Zdenek Dohnal - 1.27.0-1 +- 1.27.0 +- add post scriptlet for update + +* Wed Jan 22 2020 Zdenek Dohnal - 1.22.5-13 +- fix build with GCC 10 and remove old obsoletes + +* Fri Jan 17 2020 Marek Kasik - 1.22.5-11 +- Rebuild for poppler-0.84.0 + +* Wed Jan 15 2020 Zdenek Dohnal - 1.22.5-11 +- add buildrequires fro systemd-rpm-macros + +* Tue Nov 26 2019 Zdenek Dohnal - 1.22.5-10 +- 1776271 - Updated cups-browsed in RHEL 7.7 leaks sockets + +* Tue Nov 19 2019 Zdenek Dohnal - 1.22.5-9 +- rebuilt for qpdf-9.1.0 + +* Tue Oct 22 2019 Zdenek Dohnal - 1.22.5-8 +- 1756726 - Epson ET 7700 reports pwg support, but pwg does not work + +* Wed Oct 09 2019 Zdenek Dohnal - 1.22.5-7 +- gs 9.27 now uses setfilladjust2 + +* Tue Sep 17 2019 Zdenek Dohnal - 1.22.5-6 +- ftbfs with qpdf-9.0.0 +- pdftopdf output should not be encrypted + +* Wed Sep 11 2019 Zdenek Dohnal - 1.22.5-5 +- require colord, because it is needed for ICC profiles for filters + +* Tue Aug 13 2019 Zdenek Dohnal - 1.22.5-4 +- 1740122 - foomatic-rip segfaults when env variable PRINTER is not defined + +* Wed Aug 07 2019 Zdenek Dohnal - 1.22.5-3 +- remove unneeded scriptlet + +* Wed Jul 24 2019 Fedora Release Engineering - 1.22.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Mon Apr 08 2019 Zdenek Dohnal - 1.22.5-1 +- 1.22.5 + +* Tue Mar 26 2019 Zdenek Dohnal - 1.22.3-1 +- 1.22.3 + +* Fri Feb 01 2019 Zdenek Dohnal - 1.22.0-4 +- cups-brf needs to be run as root + +* Thu Jan 31 2019 Fedora Release Engineering - 1.22.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Fri Jan 25 2019 Marek Kasik - 1.22.0-2 +- Rebuild for poppler-0.73.0 + +* Fri Jan 25 2019 Zdenek Dohnal - 1.22.0-1 +- 1.22.0 + +* Mon Jan 14 2019 Björn Esser - 1.21.6-2 +- Rebuilt for libcrypt.so.2 (#1666033) + +* Tue Jan 08 2019 Zdenek Dohnal - 1.21.6-1 +- 1.21.6 + +* Thu Dec 13 2018 Zdenek Dohnal - 1.21.5-1 +- 1.21.5 + +* Mon Nov 12 2018 Zdenek Dohnal - 1.21.2-4 +- links in manpages are wrong + +* Mon Sep 24 2018 Zdenek Dohnal - 1.21.2-3 +- 1632267 - cups-filters needs to obsolete ghostscript-cups and foomatic-filters +- rebuilt for qpdf-8.2.1 + +* Fri Sep 21 2018 Zdenek Dohnal - 1.21.2-2 +- 1628255 - cups-filters: Sticky EOF behavior in glibc breaks descriptor concatenation using dup2 (breaks printing) + +* Mon Sep 10 2018 Zdenek Dohnal - 1.21.2-1 +- 1.21.2 + +* Tue Aug 14 2018 Marek Kasik - 1.20.3-7 +- Rebuild for poppler-0.67.0 + +* Tue Jul 24 2018 Zdenek Dohnal - 1.20.3-6 +- correcting license + +* Thu Jul 12 2018 Fedora Release Engineering - 1.20.3-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Mon Jun 25 2018 Zdenek Dohnal - 1.20.3-4 +- rebuilt for new qpdf-8.1.0 + +* Tue Jun 12 2018 Zdenek Dohnal - 1.20.3-3 +- hybrid pdftops filter requires poppler and ghostscript for run + +* Tue Jun 12 2018 Zdenek Dohnal - 1.20.3-2 +- cups-browsed needs to have cups.service to run + +* Fri Apr 13 2018 Zdenek Dohnal - 1.20.3-1 +- 1.20.3 + +* Wed Apr 04 2018 Zdenek Dohnal - 1.20.2-1 +- 1.20.2 +- fixing discovering of remote CUPS queues and LDAP queues +- dependency on poppler-utils is now only recommended + +* Fri Mar 23 2018 Marek Kasik - 1.20.1-4 +- Rebuild for poppler-0.63.0 + +* Wed Mar 07 2018 Zdenek Dohnal - 1.20.1-3 +- Rebuilt for qpdf-8.0.2 + +* Mon Mar 05 2018 Zdenek Dohnal - 1.20.1-2 +- 1.20.1 + +* Wed Feb 28 2018 Zdenek Dohnal - 1.20.0-8 +- add explicit soname -> warning about soname change + +* Wed Feb 21 2018 Zdenek Dohnal - 1.20.0-7 +- libjpeg is shipped in libjpeg-turbo and pkgconfig in pkgconf-pkg-config + +* Mon Feb 19 2018 Zdenek Dohnal - 1.20.0-6 +- gcc and gcc-c++ is no longer in buildroot by default + +* Wed Feb 14 2018 David Tardon - 1.20.0-5 +- rebuild for poppler 0.62.0 + +* Fri Feb 09 2018 Igor Gnatenko - 1.20.0-4 +- Escape macros in %%changelog + +* Thu Feb 08 2018 Zdenek Dohnal - 1.20.0-3 +- remove old stuff https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/thread/MRWOMRZ6KPCV25EFHJ2O67BCCP3L4Y6N/ + +* Wed Feb 07 2018 Fedora Release Engineering - 1.20.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Tue Jan 30 2018 Zdenek Dohnal - 1.20.0-1 +- Rebase to 1.20.0 + +* Sat Jan 20 2018 Björn Esser - 1.19.0-2 +- Rebuilt for switch to libxcrypt + +* Tue Jan 16 2018 Zdenek Dohnal - 1.19.0-1 +- Rebase to 1.19.0 + +* Thu Jan 11 2018 Zdenek Dohnal - 1.17.9-5 +- adding build dependency on ghostscript because of its package changes + +* Tue Jan 02 2018 Zdenek Dohnal - 1.17.9-4 +- 1529680 - set CreateIPPPrintQueues to ALL and LocalRemoteCUPSQueueNaming to RemoteName + +* Mon Nov 20 2017 Zdenek Dohnal - 1.17.9-3 +- fixing patch for upstream issue 1413 + +* Wed Nov 08 2017 David Tardon - 1.17.9-2 +- rebuild for poppler 0.61.0 + +* Wed Oct 18 2017 Zdenek Dohnal - 1.17.9-1 +- rebase to 1.17.9 + +* Mon Oct 09 2017 Zdenek Dohnal - 1.17.8-4 +- removing Provides ghostscript-cups and foomatic-filters + +* Fri Oct 06 2017 David Tardon - 1.17.8-3 +- rebuild for poppler 0.60.1 + +* Fri Oct 06 2017 Zdenek Dohnal - 1.17.8-2 +- upstream 1413 - Propagation of location doesn't work + +* Tue Oct 03 2017 Zdenek Dohnal - 1.17.8-1 +- rebase to 1.17.8 + +* Tue Sep 19 2017 Zdenek Dohnal - 1.17.7-1 +- rebase to 1.17.7 + +* Fri Sep 08 2017 David Tardon - 1.17.2-2 +- rebuild for poppler 0.59.0 + +* Wed Sep 06 2017 Zdenek Dohnal - 1.17.2-1 +- rebase to 1.17.2 + +* Tue Aug 22 2017 Zdenek Dohnal - 1.16.3-1 +- rebase to 1.16.3 + +* Mon Aug 14 2017 Zdenek Dohnal - 1.16.1-1 +- rebase to 1.16.1 + +* Thu Aug 10 2017 Zdenek Dohnal - 1.16.0-2 - rebuilt for qpdf-libs -* Wed May 31 2017 Zdenek Dohnal - 1.13.4-2 +* Mon Aug 07 2017 Zdenek Dohnal - 1.16.0-1 +- rebase to 1.16.0 + +* Thu Aug 03 2017 David Tardon - 1.14.1-5 +- rebuild for poppler 0.57.0 + +* Wed Aug 02 2017 Fedora Release Engineering - 1.14.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 1.14.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Mon Jul 03 2017 Jonathan Wakely - 1.14.1-2 +- Rebuilt for Boost 1.64 + +* Fri Jun 30 2017 Zdenek Dohnal - 1.14.1-1 +- rebase to 1.14.1 + +* Thu Jun 29 2017 Zdenek Dohnal - 1.14.0-3 +- update python Requires/BuildRequires accordingly to Fedora Guidelines for Python (python-cups -> python3-cups) + +* Wed May 31 2017 Zdenek Dohnal - 1.14.0-2 - removing BuildRequires: mupdf +* Wed May 17 2017 Zdenek Dohnal - 1.14.0-1 +- rebase to 1.14.0 + +* Fri Apr 28 2017 Zdenek Dohnal - 1.13.5-1 +- rebase to 1.13.5 + +* Tue Mar 28 2017 David Tardon - 1.13.4-2 +- rebuild for poppler 0.53.0 + * Fri Feb 24 2017 Zdenek Dohnal - 1.13.4-1 - rebase to 1.13.4 - 1426567 - Added queues are not marked as remote ones @@ -359,7 +943,7 @@ fi - Rebuild for poppler-0.43.0 * Thu Mar 24 2016 Zdenek Dohnal - 1.8.3-1 -- Update to 1.8.3, adding cupsfilters-ghostscript.convs to %files +- Update to 1.8.3, adding cupsfilters-ghostscript.convs to %%files * Fri Feb 12 2016 Jiri Popelka - 1.8.2-1 - 1.8.2 diff --git a/foomatic-ripdie-error.patch b/foomatic-ripdie-error.patch new file mode 100644 index 0000000..2ef0166 --- /dev/null +++ b/foomatic-ripdie-error.patch @@ -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); diff --git a/foomaticrip-reject-unknown-values.patch b/foomaticrip-reject-unknown-values.patch new file mode 100644 index 0000000..3a90e74 --- /dev/null +++ b/foomaticrip-reject-unknown-values.patch @@ -0,0 +1,188 @@ +From 41c5f2f6139e4d3693c2483ee4281202a80ae451 Mon Sep 17 00:00:00 2001 +From: zdohnal +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