1 # --- T2-COPYRIGHT-NOTE-BEGIN ---
2 # T2 SDE: package/*/cups-filters/CVE-2023-24805.patch
3 # Copyright (C) 2023 The T2 SDE Project
5 # This Copyright note is generated by scripts/Create-CopyPatch,
6 # more information can be found in the files COPYING and README.
8 # This patch file is dual-licensed. It is available under the license the
9 # patched project is licensed under, as long as it is an OpenSource license
10 # as defined at http://www.opensource.org/ (e.g. BSD, X11) or under the terms
11 # of the GNU General Public License version 2 as used by the T2 SDE.
12 # --- T2-COPYRIGHT-NOTE-END ---
14 Patch-Source: https://github.com/OpenPrinting/cups-filters/commit/93e60d3df358c0ae6f3dba79e1c9684657683d89
16 From 93e60d3df358c0ae6f3dba79e1c9684657683d89 Mon Sep 17 00:00:00 2001
17 From: Till Kamppeter <till.kamppeter@gmail.com>
18 Date: Wed, 17 May 2023 11:11:29 +0200
19 Subject: [PATCH] beh backend: Use execv() instead of system() - CVE-2023-24805
21 With execv() command line arguments are passed as separate strings and
22 not the full command line in a single string. This prevents arbitrary
23 command execution by escaping the quoting of the arguments in a job
24 with a forged job title.
26 In addition, done the following fixes and improvements:
28 - Do not allow '/' in the scheme of the URI (= backend executable
29 name), to assure that only backends inside /usr/lib/cups/backend/
32 - URI must have ':', to split off scheme, otherwise error out.
34 - Check return value of snprintf() to create call path for backend, to
35 error out on truncation of a too long scheme or on complete failure
36 due to a completely odd scheme.
38 - Use strncat() instead of strncpy() for getting scheme from URI, the latter
39 does not require setting terminating zero byte in case of truncation.
41 - Also exclude "." or ".." as scheme, as directories are not valid CUPS
44 - Do not use fprintf() in sigterm_handler(), to not interfere with a
45 fprintf() which could be running in the main process when
46 sigterm_handler() is triggered.
48 - Use "static volatile int" for global variable job_canceled.
50 backend/beh.c | 107 +++++++++++++++++++++++++++++++++++++++-----------
51 1 file changed, 84 insertions(+), 23 deletions(-)
53 diff --git a/backend/beh.c b/backend/beh.c
54 index 225fd27d5..8d51235b1 100644
58 #include "backend-private.h"
59 #include <cups/array.h>
61 +#include <sys/wait.h>
67 -static int job_canceled = 0; /* Set to 1 on SIGTERM */
68 +static volatile int job_canceled = 0; /* Set to 1 on SIGTERM */
72 @@ -213,21 +214,40 @@ call_backend(char *uri, /* I - URI of final destination */
73 char **argv, /* I - Command-line arguments */
74 char *filename) { /* I - File name of input data */
75 const char *cups_serverbin; /* Location of programs */
76 + char *backend_argv[8]; /* Arguments for backend */
77 char scheme[1024], /* Scheme from URI */
78 *ptr, /* Pointer into scheme */
79 - cmdline[65536]; /* Backend command line */
81 + backend_path[2048]; /* Backend path */
82 + int pid = 0, /* Process ID of backend */
83 + wait_pid, /* Process ID from wait() */
84 + wait_status, /* Status from child */
89 * Build the backend command line...
92 - strncpy(scheme, uri, sizeof(scheme) - 1);
93 - if (strlen(uri) > 1023)
94 - scheme[1023] = '\0';
96 + strncat(scheme, uri, sizeof(scheme) - 1);
97 if ((ptr = strchr(scheme, ':')) != NULL)
102 + "ERROR: beh: Invalid URI, no colon (':') to mark end of scheme part.\n");
103 + exit (CUPS_BACKEND_FAILED);
105 + if (strchr(scheme, '/')) {
107 + "ERROR: beh: Invalid URI, scheme contains a slash ('/').\n");
108 + exit (CUPS_BACKEND_FAILED);
110 + if (!strcmp(scheme, ".") || !strcmp(scheme, "..")) {
112 + "ERROR: beh: Invalid URI, scheme (\"%s\") is a directory.\n",
114 + exit (CUPS_BACKEND_FAILED);
116 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
117 cups_serverbin = CUPS_SERVERBIN;
119 @@ -235,16 +255,29 @@ call_backend(char *uri, /* I - URI of final destination */
121 "ERROR: beh: Direct output into a file not supported.\n");
122 exit (CUPS_BACKEND_FAILED);
124 - snprintf(cmdline, sizeof(cmdline),
125 - "%s/backend/%s '%s' '%s' '%s' '%s' '%s' %s",
126 - cups_serverbin, scheme, argv[1], argv[2], argv[3],
127 - /* Apply number of copies only if beh was called with a
128 - file name and not with the print data in stdin, as
129 - backends should handle copies only if they are called
130 - with a file name */
131 - (argc == 6 ? "1" : argv[4]),
132 - argv[5], filename);
135 + backend_argv[0] = uri;
136 + backend_argv[1] = argv[1];
137 + backend_argv[2] = argv[2];
138 + backend_argv[3] = argv[3];
139 + /* Apply number of copies only if beh was called with a file name
140 + and not with the print data in stdin, as backends should handle
141 + copies only if they are called with a file name */
142 + backend_argv[4] = (argc == 6 ? "1" : argv[4]);
143 + backend_argv[5] = argv[5];
144 + backend_argv[6] = filename;
145 + backend_argv[7] = NULL;
147 + bytes = snprintf(backend_path, sizeof(backend_path),
148 + "%s/backend/%s", cups_serverbin, scheme);
149 + if (bytes < 0 || bytes >= sizeof(backend_path))
152 + "ERROR: beh: Invalid scheme (\"%s\"), could not determing backend path.\n",
154 + return (CUPS_BACKEND_FAILED);
158 * Overwrite the device URI and run the actual backend...
159 @@ -253,18 +286,44 @@ call_backend(char *uri, /* I - URI of final destination */
160 setenv("DEVICE_URI", uri, 1);
163 - "DEBUG: beh: Executing backend command line \"%s\"...\n",
165 + "DEBUG: beh: Executing backend command line \"%s '%s' '%s' '%s' '%s' '%s' %s\"...\n",
166 + backend_path, backend_argv[1], backend_argv[2], backend_argv[3],
167 + backend_argv[4], backend_argv[5], backend_argv[6]);
169 "DEBUG: beh: Using device URI: %s\n",
172 - retval = system(cmdline) >> 8;
173 + if ((pid = fork()) == 0) {
175 + * Child comes here...
178 + /* Run the backend */
179 + execv(backend_path, backend_argv);
182 fprintf(stderr, "ERROR: Unable to execute backend command line: %s\n",
186 + } else if (pid < 0) {
191 + return (CUPS_BACKEND_FAILED);
194 + while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR);
196 + if (wait_pid >= 0 && wait_status) {
197 + if (WIFEXITED(wait_status))
198 + retval = WEXITSTATUS(wait_status);
199 + else if (WTERMSIG(wait_status) != SIGTERM)
200 + retval = WTERMSIG(wait_status);
208 @@ -277,8 +336,10 @@ static void
209 sigterm_handler(int sig) { /* I - Signal number (unused) */
213 - "DEBUG: beh: Job canceled.\n");
214 + const char * const msg = "DEBUG: beh: Job canceled.\n";
215 + /* The if() is to eliminate the return value and silence the warning
216 + about an unused return value. */
217 + if (write(2, msg, strlen(msg)));
220 _exit(CUPS_BACKEND_OK);