Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : printing command routines
4 : Copyright (C) Andrew Tridgell 1992-2000
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "lib/util/util_str_escape.h"
22 : #include "printing.h"
23 : #include "smbd/proto.h"
24 : #include "source3/lib/substitute.h"
25 :
26 : extern userdom_struct current_user_info;
27 :
28 : /****************************************************************************
29 : Run a given print command
30 : a null terminated list of value/substitute pairs is provided
31 : for local substitution strings
32 : ****************************************************************************/
33 0 : static int print_run_command(int snum, const char* printername, bool do_sub,
34 : const char *command, int *outfd, ...)
35 : {
36 0 : const struct loadparm_substitution *lp_sub =
37 0 : loadparm_s3_global_substitution();
38 : char *syscmd;
39 : char *arg;
40 : int ret;
41 0 : TALLOC_CTX *ctx = talloc_tos();
42 : va_list ap;
43 0 : va_start(ap, outfd);
44 :
45 : /* check for a valid system printername and valid command to run */
46 :
47 0 : if ( !printername || !*printername ) {
48 0 : va_end(ap);
49 0 : return -1;
50 : }
51 :
52 0 : if (!command || !*command) {
53 0 : va_end(ap);
54 0 : return -1;
55 : }
56 :
57 0 : syscmd = talloc_strdup(ctx, command);
58 0 : if (!syscmd) {
59 0 : va_end(ap);
60 0 : return -1;
61 : }
62 :
63 0 : DBG_DEBUG("Incoming command '%s'\n", syscmd);
64 :
65 0 : while ((arg = va_arg(ap, char *))) {
66 0 : char *value = va_arg(ap,char *);
67 0 : syscmd = talloc_string_sub(ctx, syscmd, arg, value);
68 0 : if (!syscmd) {
69 0 : va_end(ap);
70 0 : return -1;
71 : }
72 : }
73 0 : va_end(ap);
74 :
75 0 : syscmd = talloc_string_sub(ctx, syscmd, "%p", printername);
76 0 : if (!syscmd) {
77 0 : return -1;
78 : }
79 :
80 0 : syscmd = lpcfg_substituted_string(ctx, lp_sub, syscmd);
81 0 : if (syscmd == NULL) {
82 0 : return -1;
83 : }
84 :
85 0 : if (do_sub && snum != -1) {
86 0 : syscmd = talloc_sub_advanced(ctx,
87 0 : lp_servicename(talloc_tos(), lp_sub, snum),
88 : current_user_info.unix_name,
89 : "",
90 : get_current_gid(NULL),
91 : syscmd);
92 0 : if (!syscmd) {
93 0 : return -1;
94 : }
95 : }
96 :
97 0 : ret = smbrun_no_sanitize(syscmd, outfd, NULL);
98 :
99 0 : DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
100 :
101 0 : return ret;
102 : }
103 :
104 :
105 : /****************************************************************************
106 : delete a print job
107 : ****************************************************************************/
108 0 : static int generic_job_delete( const char *sharename, const char *lprm_command, struct printjob *pjob)
109 : {
110 : fstring jobstr;
111 :
112 : /* need to delete the spooled entry */
113 0 : fstr_sprintf(jobstr, "%d", pjob->sysjob);
114 0 : return print_run_command( -1, sharename, False, lprm_command, NULL,
115 : "%j", jobstr,
116 : "%T", http_timestring(talloc_tos(), pjob->starttime),
117 : NULL);
118 : }
119 :
120 : /****************************************************************************
121 : pause a job
122 : ****************************************************************************/
123 0 : static int generic_job_pause(int snum, struct printjob *pjob)
124 : {
125 0 : const struct loadparm_substitution *lp_sub =
126 0 : loadparm_s3_global_substitution();
127 : fstring jobstr;
128 :
129 : /* need to pause the spooled entry */
130 0 : fstr_sprintf(jobstr, "%d", pjob->sysjob);
131 0 : return print_run_command(snum, lp_printername(talloc_tos(), lp_sub, snum), True,
132 : lp_lppause_command(snum), NULL,
133 : "%j", jobstr,
134 : NULL);
135 : }
136 :
137 : /****************************************************************************
138 : resume a job
139 : ****************************************************************************/
140 0 : static int generic_job_resume(int snum, struct printjob *pjob)
141 : {
142 0 : const struct loadparm_substitution *lp_sub =
143 0 : loadparm_s3_global_substitution();
144 : fstring jobstr;
145 :
146 : /* need to pause the spooled entry */
147 0 : fstr_sprintf(jobstr, "%d", pjob->sysjob);
148 0 : return print_run_command(snum, lp_printername(talloc_tos(), lp_sub, snum), True,
149 : lp_lpresume_command(snum), NULL,
150 : "%j", jobstr,
151 : NULL);
152 : }
153 :
154 : /****************************************************************************
155 : get the current list of queued jobs
156 : ****************************************************************************/
157 0 : static int generic_queue_get(const char *printer_name,
158 : enum printing_types printing_type,
159 : char *lpq_command,
160 : print_queue_struct **q,
161 : print_status_struct *status)
162 : {
163 : char **qlines;
164 : int fd;
165 : int numlines, i, qcount;
166 0 : print_queue_struct *queue = NULL;
167 :
168 : /* never do substitution when running the 'lpq command' since we can't
169 : get it right when using the background update daemon. Make the caller
170 : do it before passing off the command string to us here. */
171 :
172 0 : print_run_command(-1, printer_name, False, lpq_command, &fd, NULL);
173 :
174 0 : if (fd == -1) {
175 0 : DEBUG(5,("generic_queue_get: Can't read print queue status for printer %s\n",
176 : printer_name ));
177 0 : return 0;
178 : }
179 :
180 0 : numlines = 0;
181 0 : qlines = fd_lines_load(fd, &numlines,0,NULL);
182 0 : close(fd);
183 :
184 : /* turn the lpq output into a series of job structures */
185 0 : qcount = 0;
186 0 : ZERO_STRUCTP(status);
187 0 : if (numlines && qlines) {
188 0 : queue = SMB_MALLOC_ARRAY(print_queue_struct, numlines+1);
189 0 : if (!queue) {
190 0 : TALLOC_FREE(qlines);
191 0 : *q = NULL;
192 0 : return 0;
193 : }
194 0 : memset(queue, '\0', sizeof(print_queue_struct)*(numlines+1));
195 :
196 0 : for (i=0; i<numlines; i++) {
197 : /* parse the line */
198 0 : if (parse_lpq_entry(printing_type,qlines[i],
199 0 : &queue[qcount],status,qcount==0)) {
200 0 : qcount++;
201 : }
202 : }
203 : }
204 :
205 0 : TALLOC_FREE(qlines);
206 0 : *q = queue;
207 0 : return qcount;
208 : }
209 :
210 0 : static const char *replace_print_cmd_J(TALLOC_CTX *mem_ctx,
211 : const char *orig_cmd,
212 : const char *unsafe_jobname,
213 : const char *fallback_jobname)
214 : {
215 0 : char *cmd = NULL;
216 0 : bool modified = false;
217 0 : bool masked = false;
218 0 : bool mixed_fallback = false;
219 :
220 : /*
221 : * This replaces unsafe characters with '_'.
222 : * We also mask forward and backslash here.
223 : *
224 : * Then it replaces %J with an single quoted
225 : * version of the masked jobname or it falls
226 : * back to fallback_jobname is the print command
227 : * uses strange mixed quoting.
228 : */
229 :
230 : #define JOBNAME_UNSAFE_CHARACTERS \
231 : STRING_SUB_UNSAFE_CHARACTERS "/\\"
232 :
233 0 : cmd = talloc_string_sub_unsafe(mem_ctx,
234 : orig_cmd,
235 : 'J',
236 : unsafe_jobname,
237 : JOBNAME_UNSAFE_CHARACTERS,
238 : '_',
239 : fallback_jobname,
240 : &modified,
241 : &masked,
242 : &mixed_fallback);
243 0 : if (cmd == NULL) {
244 0 : return NULL;
245 : }
246 :
247 : /*
248 : * The caller already checked talloc_string_sub_mixed_quoting()
249 : * and warned the admin, so we don't check mixed_fallback
250 : * here
251 : */
252 :
253 0 : return cmd;
254 : }
255 :
256 : /****************************************************************************
257 : Submit a file for printing - called from print_job_end()
258 : ****************************************************************************/
259 :
260 0 : static int generic_job_submit(int snum, struct printjob *pjob,
261 : enum printing_types printing_type,
262 : char *lpq_cmd)
263 : {
264 0 : int ret = -1;
265 0 : const struct loadparm_substitution *lp_sub =
266 0 : loadparm_s3_global_substitution();
267 0 : char *current_directory = NULL;
268 0 : char *print_directory = NULL;
269 0 : char *wd = NULL;
270 0 : char *p = NULL;
271 0 : const char *print_cmd = NULL;
272 0 : TALLOC_CTX *ctx = talloc_tos();
273 : fstring job_page_count, job_size;
274 : print_queue_struct *q;
275 : print_status_struct status;
276 0 : const char *jobname = "No Document Name";
277 :
278 : /* we print from the directory path to give the best chance of
279 : parsing the lpq output */
280 0 : wd = sys_getwd();
281 0 : if (!wd) {
282 0 : return -1;
283 : }
284 :
285 0 : current_directory = talloc_strdup(ctx, wd);
286 0 : SAFE_FREE(wd);
287 :
288 0 : if (!current_directory) {
289 0 : return -1;
290 : }
291 0 : print_directory = talloc_strdup(ctx, pjob->filename);
292 0 : if (!print_directory) {
293 0 : return -1;
294 : }
295 0 : p = strrchr_m(print_directory,'/');
296 0 : if (!p) {
297 0 : return -1;
298 : }
299 0 : *p++ = 0;
300 :
301 0 : if (chdir(print_directory) != 0) {
302 0 : return -1;
303 : }
304 :
305 0 : if (pjob->jobname[0] != '\0') {
306 0 : jobname = pjob->jobname;
307 : }
308 :
309 0 : print_cmd = lp_print_command(snum);
310 0 : if (print_cmd != NULL) {
311 0 : const char *invalid_jobname = "__CVE-2026-4480_FallbackJobname__";
312 :
313 0 : if (talloc_string_sub_mixed_quoting(print_cmd, 'J')) {
314 : /*
315 : * The admin used a strange mixture of
316 : * single and double quotes, fallback
317 : * to InvalidDocumentName and warn about
318 : * it, so that the admin can adjust to
319 : * the use single quotes directly around %J,
320 : * e.g. '%J'.
321 : */
322 0 : jobname = invalid_jobname;
323 0 : D_WARNING("CVE-2026-4480: printer %s "
324 : "strange quoting in 'print command', "
325 : "falling back to jobname=%s, "
326 : "use testparm to fix the configuration\n",
327 : lp_printername(talloc_tos(), lp_sub, snum),
328 : invalid_jobname);
329 : }
330 :
331 0 : print_cmd = replace_print_cmd_J(ctx,
332 : print_cmd,
333 : jobname,
334 : invalid_jobname);
335 0 : if (!print_cmd) {
336 0 : ret = -1;
337 0 : goto out;
338 : }
339 : }
340 0 : fstr_sprintf(job_page_count, "%d", pjob->page_count);
341 0 : fstr_sprintf(job_size, "%zu", pjob->size);
342 :
343 : /* send it to the system spooler */
344 0 : ret = print_run_command(snum, lp_printername(talloc_tos(), lp_sub, snum), True,
345 : print_cmd, NULL,
346 : "%s", p,
347 : "%f", p,
348 : "%z", job_size,
349 : "%c", job_page_count,
350 : NULL);
351 0 : if (ret != 0) {
352 0 : ret = -1;
353 0 : goto out;
354 : }
355 :
356 : /*
357 : * check the queue for the newly submitted job, this allows us to
358 : * determine the backend job identifier (sysjob).
359 : */
360 0 : pjob->sysjob = -1;
361 0 : ret = generic_queue_get(lp_printername(talloc_tos(), lp_sub, snum),
362 : printing_type, lpq_cmd, &q, &status);
363 0 : if (ret > 0) {
364 : int i;
365 0 : for (i = 0; i < ret; i++) {
366 0 : if (strcmp(q[i].fs_file, p) == 0) {
367 0 : char *le_jobname =
368 0 : log_escape(talloc_tos(), jobname);
369 :
370 0 : pjob->sysjob = q[i].sysjob;
371 0 : DEBUG(5, ("new job %u (%s) matches sysjob %d\n",
372 : pjob->jobid, le_jobname, pjob->sysjob));
373 :
374 0 : TALLOC_FREE(le_jobname);
375 0 : break;
376 : }
377 : }
378 0 : SAFE_FREE(q);
379 0 : ret = 0;
380 : }
381 0 : if (pjob->sysjob == -1) {
382 0 : char *le_jobname = log_escape(talloc_tos(), jobname);
383 :
384 0 : DEBUG(2, ("failed to get sysjob for job %u (%s), tracking as "
385 : "Unix job\n", pjob->jobid, le_jobname));
386 :
387 0 : TALLOC_FREE(le_jobname);
388 : }
389 :
390 :
391 0 : out:
392 :
393 0 : if (chdir(current_directory) == -1) {
394 0 : smb_panic("chdir failed in generic_job_submit");
395 : }
396 0 : TALLOC_FREE(current_directory);
397 0 : return ret;
398 : }
399 :
400 : /****************************************************************************
401 : pause a queue
402 : ****************************************************************************/
403 0 : static int generic_queue_pause(int snum)
404 : {
405 0 : const struct loadparm_substitution *lp_sub =
406 0 : loadparm_s3_global_substitution();
407 :
408 0 : return print_run_command(snum, lp_printername(talloc_tos(), lp_sub, snum), True,
409 : lp_queuepause_command(snum), NULL, NULL);
410 : }
411 :
412 : /****************************************************************************
413 : resume a queue
414 : ****************************************************************************/
415 0 : static int generic_queue_resume(int snum)
416 : {
417 0 : const struct loadparm_substitution *lp_sub =
418 0 : loadparm_s3_global_substitution();
419 :
420 0 : return print_run_command(snum, lp_printername(talloc_tos(), lp_sub, snum), True,
421 : lp_queueresume_command(snum), NULL, NULL);
422 : }
423 :
424 : /****************************************************************************
425 : * Generic printing interface definitions...
426 : ***************************************************************************/
427 :
428 : struct printif generic_printif =
429 : {
430 : DEFAULT_PRINTING,
431 : generic_queue_get,
432 : generic_queue_pause,
433 : generic_queue_resume,
434 : generic_job_delete,
435 : generic_job_pause,
436 : generic_job_resume,
437 : generic_job_submit,
438 : };
439 :
|