summaryrefslogtreecommitdiff
path: root/net-print/cups-filters/files/cups-filters-1.28.17-CVE-2023-24805.patch
blob: 58b562504d0dc6620f75d4060fb2d5040bf85dae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
Modified version from:

  https://packages.debian.org/de/sid/cups-filters

  From: Thorsten Alteholz <debian@alteholz.de>
  Date: Fri, 19 May 2023 10:49:35 +0200
  Subject: fix CVE-2023-24805

Original patch:

https://github.com/OpenPrinting/cups-filters/commit/8f274035756c04efeb77eb654e9d4c4447287d65

From 8f274035756c04efeb77eb654e9d4c4447287d65 Mon Sep 17 00:00:00 2001
From: Till Kamppeter <till.kamppeter@gmail.com>
Date: Wed, 17 May 2023 11:12:37 +0200
Subject: [PATCH] Merge pull request from GHSA-gpxc-v2m8-fr3x

* beh backend: Use execv() instead of system() - CVE-2023-24805

With execv() command line arguments are passed as separate strings and
not the full command line in a single string. This prevents arbitrary
command execution by escaping the quoting of the arguments in a job
with forged job title.

* beh backend: Extra checks against odd/forged input - CVE-2023-24805

- Do not allow '/' in the scheme of the URI (= backend executable
  name), to assure that only backends inside /usr/lib/cups/backend/
  are used.

- Pre-define scheme buffer to empty string, to be defined for case of
  uri being NULL.

- URI must have ':', to split off scheme, otherwise error.

- Check return value of snprintf() to create call path for backend, to
  error out on truncation of a too long scheme or on complete failure
  due to a completely odd scheme.

* beh backend: Further improvements - CVE-2023-24805

- Use strncat() instead of strncpy() for getting scheme from URI, the latter
  does not require setting terminating zero byte in case of truncation.

- Also exclude "." or ".." as scheme, as directories are not valid CUPS
  backends.

- Do not use fprintf() in sigterm_handler(), to not interfere with a
  fprintf() which could be running in the main process when
  sigterm_handler() is triggered.

- Use "static volatile int" for global variable job_canceled.

---
 backend/beh.c | 107 +++++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 84 insertions(+), 23 deletions(-)

diff --git a/backend/beh.c b/backend/beh.c
index 225fd27..8d51235 100644
--- a/backend/beh.c
+++ b/backend/beh.c
@@ -22,12 +22,13 @@
 #include "backend-private.h"
 #include <cups/array.h>
 #include <ctype.h>
+#include <sys/wait.h>
 
 /*
  * Local globals...
  */
 
-static int		job_canceled = 0; /* Set to 1 on SIGTERM */
+static volatile int	job_canceled = 0; /* Set to 1 on SIGTERM */
 
 /*
  * Local functions...
@@ -213,21 +214,40 @@ call_backend(char *uri,                 /* I - URI of final destination */
 	     char **argv,		/* I - Command-line arguments */
 	     char *filename) {          /* I - File name of input data */
   const char	*cups_serverbin;	/* Location of programs */
+  char          *backend_argv[8];	/* Arguments for backend */
   char		scheme[1024],           /* Scheme from URI */
                 *ptr,			/* Pointer into scheme */
-		cmdline[65536];		/* Backend command line */
-  int           retval;
+		backend_path[2048];	/* Backend path */
+  int           pid = 0, 		/* Process ID of backend */
+                wait_pid,		/* Process ID from wait() */
+                wait_status, 		/* Status from child */
+                retval = 0;
+  int           bytes;
 
  /*
   * Build the backend command line...
   */
 
-  strncpy(scheme, uri, sizeof(scheme) - 1);
-  if (strlen(uri) > 1023)
-    scheme[1023] = '\0';
+  scheme[0] = '\0';
+  strncat(scheme, uri, sizeof(scheme) - 1);
   if ((ptr = strchr(scheme, ':')) != NULL)
     *ptr = '\0';
-
+  else {
+    fprintf(stderr,
+	    "ERROR: beh: Invalid URI, no colon (':') to mark end of scheme part.\n");
+    exit (CUPS_BACKEND_FAILED);
+  }
+  if (strchr(scheme, '/')) {
+    fprintf(stderr,
+	    "ERROR: beh: Invalid URI, scheme contains a slash ('/').\n");
+    exit (CUPS_BACKEND_FAILED);
+  }
+  if (!strcmp(scheme, ".") || !strcmp(scheme, "..")) {
+    fprintf(stderr,
+	    "ERROR: beh: Invalid URI, scheme (\"%s\") is a directory.\n",
+	    scheme);
+    exit (CUPS_BACKEND_FAILED);
+  }
   if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
     cups_serverbin = CUPS_SERVERBIN;
 
@@ -235,16 +255,29 @@ call_backend(char *uri,                 /* I - URI of final destination */
     fprintf(stderr,
 	    "ERROR: beh: Direct output into a file not supported.\n");
     exit (CUPS_BACKEND_FAILED);
-  } else
-    snprintf(cmdline, sizeof(cmdline),
-	     "%s/backend/%s '%s' '%s' '%s' '%s' '%s' %s",
-	     cups_serverbin, scheme, argv[1], argv[2], argv[3],
-	     /* Apply number of copies only if beh was called with a
-		file name and not with the print data in stdin, as
-	        backends should handle copies only if they are called
-	        with a file name */
-	     (argc == 6 ? "1" : argv[4]),
-	     argv[5], filename);
+  }
+
+  backend_argv[0] = uri;
+  backend_argv[1] = argv[1];
+  backend_argv[2] = argv[2];
+  backend_argv[3] = argv[3];
+  /* Apply number of copies only if beh was called with a file name
+     and not with the print data in stdin, as backends should handle
+     copies only if they are called with a file name */
+  backend_argv[4] = (argc == 6 ? "1" : argv[4]);
+  backend_argv[5] = argv[5];
+  backend_argv[6] = filename;
+  backend_argv[7] = NULL;
+
+  bytes = snprintf(backend_path, sizeof(backend_path),
+		   "%s/backend/%s", cups_serverbin, scheme);
+  if (bytes < 0 || bytes >= sizeof(backend_path))
+  {
+    fprintf(stderr,
+	    "ERROR: beh: Invalid scheme (\"%s\"), could not determing backend path.\n",
+	    scheme);
+    return (CUPS_BACKEND_FAILED);
+  }
 
  /*
   * Overwrite the device URI and run the actual backend...
@@ -253,18 +286,44 @@ call_backend(char *uri,                 /* I - URI of final destination */
   setenv("DEVICE_URI", uri, 1);
 
   fprintf(stderr,
-	  "DEBUG: beh: Executing backend command line \"%s\"...\n",
-	  cmdline);
+	  "DEBUG: beh: Executing backend command line \"%s '%s' '%s' '%s' '%s' '%s' %s\"...\n",
+	  backend_path, backend_argv[1], backend_argv[2], backend_argv[3],
+	  backend_argv[4], backend_argv[5], backend_argv[6]);
   fprintf(stderr,
 	  "DEBUG: beh: Using device URI: %s\n",
 	  uri);
 
-  retval = system(cmdline) >> 8;
+  if ((pid = fork()) == 0) {
+   /*
+    * Child comes here...
+    */
+
+    /* Run the backend */
+    execv(backend_path, backend_argv);
 
-  if (retval == -1)
     fprintf(stderr, "ERROR: Unable to execute backend command line: %s\n",
 	    strerror(errno));
 
+    exit(1);
+  } else if (pid < 0) {
+   /*
+    * Unable to fork!
+    */
+
+    return (CUPS_BACKEND_FAILED);
+  }
+
+  while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR);
+
+  if (wait_pid >= 0 && wait_status) {
+    if (WIFEXITED(wait_status))
+      retval = WEXITSTATUS(wait_status);
+    else if (WTERMSIG(wait_status) != SIGTERM)
+      retval = WTERMSIG(wait_status);
+    else
+      retval = 0;
+  }
+
   return (retval);
 }
 
@@ -277,8 +336,10 @@ static void
 sigterm_handler(int sig) {		/* I - Signal number (unused) */
   (void)sig;
 
-  fprintf(stderr,
-	  "DEBUG: beh: Job canceled.\n");
+  const char * const msg = "DEBUG: beh: Job canceled.\n";
+  /* The if() is to eliminate the return value and silence the warning
+     about an unused return value. */
+  if (write(2, msg, strlen(msg)));
 
   if (job_canceled)
     _exit(CUPS_BACKEND_OK);