LCOV - code coverage report
Current view: top level - source3/printing - print_generic.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-lts 011c5a9f Lines: 0 159 0.0 %
Date: 2026-05-29 04:08:37 Functions: 0 9 0.0 %

          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             : 

Generated by: LCOV version 1.13