LCOV - code coverage report
Current view: top level - source3/printing - printspoolss.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 89 188 47.3 %
Date: 2024-04-21 15:09:00 Functions: 3 5 60.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Printing routines that bridge to spoolss
       4             :    Copyright (C) Simo Sorce 2010
       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 "system/filesys.h"
      22             : #include "printing.h"
      23             : #include "rpc_client/rpc_client.h"
      24             : #include "../librpc/gen_ndr/ndr_spoolss_c.h"
      25             : #include "rpc_server/rpc_ncacn_np.h"
      26             : #include "smbd/globals.h"
      27             : #include "../libcli/security/security.h"
      28             : #include "smbd/fd_handle.h"
      29             : #include "source3/printing/rap_jobid.h"
      30             : 
      31             : struct print_file_data {
      32             :         char *svcname;
      33             :         char *docname;
      34             :         char *filename;
      35             :         struct policy_handle handle;
      36             :         uint32_t jobid;
      37             :         uint16_t rap_jobid;
      38             : };
      39             : 
      40          16 : uint16_t print_spool_rap_jobid(struct print_file_data *print_file)
      41             : {
      42          16 :         if (print_file == NULL) {
      43          16 :                 return 0;
      44             :         }
      45             : 
      46           0 :         return print_file->rap_jobid;
      47             : }
      48             : 
      49             : void print_spool_terminate(struct connection_struct *conn,
      50             :                            struct print_file_data *print_file);
      51             : 
      52             : /***************************************************************************
      53             :  * Open a Document over spoolss
      54             :  ***************************************************************************/
      55             : 
      56             : #define DOCNAME_DEFAULT "Remote Downlevel Document"
      57             : 
      58          28 : NTSTATUS print_spool_open(files_struct *fsp,
      59             :                           const char *fname,
      60             :                           uint64_t current_vuid)
      61             : {
      62           0 :         const struct loadparm_substitution *lp_sub =
      63          28 :                 loadparm_s3_global_substitution();
      64           0 :         NTSTATUS status;
      65           0 :         TALLOC_CTX *tmp_ctx;
      66           0 :         struct print_file_data *pf;
      67          28 :         struct dcerpc_binding_handle *b = NULL;
      68           0 :         struct spoolss_DevmodeContainer devmode_ctr;
      69           0 :         struct spoolss_DocumentInfoCtr info_ctr;
      70           0 :         struct spoolss_DocumentInfo1 *info1;
      71          28 :         int fd = -1;
      72           0 :         WERROR werr;
      73           0 :         mode_t mask;
      74             : 
      75          28 :         tmp_ctx = talloc_new(fsp);
      76          28 :         if (!tmp_ctx) {
      77           0 :                 return NT_STATUS_NO_MEMORY;
      78             :         }
      79             : 
      80          28 :         pf = talloc_zero(fsp, struct print_file_data);
      81          28 :         if (!pf) {
      82           0 :                 status = NT_STATUS_NO_MEMORY;
      83           0 :                 goto done;
      84             :         }
      85          28 :         pf->svcname = lp_servicename(pf, lp_sub, SNUM(fsp->conn));
      86             : 
      87             :         /* the document name is derived from the file name.
      88             :          * "Remote Downlevel Document" is added in front to
      89             :          * mimic what windows does in this case */
      90          28 :         pf->docname = talloc_strdup(pf, DOCNAME_DEFAULT);
      91          28 :         if (!pf->docname) {
      92           0 :                 status = NT_STATUS_NO_MEMORY;
      93           0 :                 goto done;
      94             :         }
      95          28 :         if (fname) {
      96          28 :                 const char *p = strrchr(fname, '/');
      97          28 :                 if (!p) {
      98          28 :                         p = fname;
      99             :                 }
     100          28 :                 pf->docname = talloc_asprintf_append(pf->docname, " %s", p);
     101          28 :                 if (!pf->docname) {
     102           0 :                         status = NT_STATUS_NO_MEMORY;
     103           0 :                         goto done;
     104             :                 }
     105             :         }
     106             : 
     107             :         /*
     108             :          * Ok, now we have to open an actual file.
     109             :          * Here is the reason:
     110             :          * We want to write the spool job to this file in
     111             :          * smbd for scalability reason (and also because
     112             :          * apparently window printer drivers can seek when
     113             :          * spooling to a file).
     114             :          * So we first create a file, and then we pass it
     115             :          * to spoolss in output_file so it can monitor and
     116             :          * take over once we call EndDocPrinter().
     117             :          * Of course we will not start writing until
     118             :          * StartDocPrinter() actually gives the ok.
     119             :          * smbd spooler files do not include a print jobid
     120             :          * path component, as the jobid is only known after
     121             :          * calling StartDocPrinter().
     122             :          */
     123             : 
     124          28 :         pf->filename = talloc_asprintf(pf, "%s/%sXXXXXX",
     125             :                                         lp_path(talloc_tos(),
     126             :                                                 lp_sub,
     127          28 :                                                 SNUM(fsp->conn)),
     128             :                                         PRINT_SPOOL_PREFIX);
     129          28 :         if (!pf->filename) {
     130           0 :                 status = NT_STATUS_NO_MEMORY;
     131           0 :                 goto done;
     132             :         }
     133          28 :         errno = 0;
     134          28 :         mask = umask(S_IRWXO | S_IRWXG);
     135          28 :         fd = mkstemp(pf->filename);
     136          28 :         umask(mask);
     137          28 :         if (fd == -1) {
     138           0 :                 if (errno == EACCES) {
     139             :                         /* Common setup error, force a report. */
     140           0 :                         DEBUG(0, ("Insufficient permissions "
     141             :                                   "to open spool file %s.\n",
     142             :                                   pf->filename));
     143             :                 } else {
     144             :                         /* Normal case, report at level 3 and above. */
     145           0 :                         DEBUG(3, ("can't open spool file %s,\n",
     146             :                                   pf->filename));
     147           0 :                         DEBUGADD(3, ("errno = %d (%s).\n",
     148             :                                      errno, strerror(errno)));
     149             :                 }
     150           0 :                 status = map_nt_error_from_unix(errno);
     151           0 :                 goto done;
     152             :         }
     153             : 
     154             :         /* now open a document over spoolss so that it does
     155             :          * all printer verification, and eventually assigns
     156             :          * a job id */
     157             : 
     158          28 :         status = rpc_pipe_open_interface(fsp->conn,
     159             :                                          &ndr_table_spoolss,
     160          28 :                                          fsp->conn->session_info,
     161          28 :                                          fsp->conn->sconn->remote_address,
     162          28 :                                          fsp->conn->sconn->local_address,
     163          28 :                                          fsp->conn->sconn->msg_ctx,
     164          28 :                                          &fsp->conn->spoolss_pipe);
     165          28 :         if (!NT_STATUS_IS_OK(status)) {
     166           0 :                 goto done;
     167             :         }
     168          28 :         b = fsp->conn->spoolss_pipe->binding_handle;
     169             : 
     170          28 :         ZERO_STRUCT(devmode_ctr);
     171             : 
     172          28 :         status = dcerpc_spoolss_OpenPrinter(b, pf, pf->svcname,
     173             :                                             "RAW", devmode_ctr,
     174             :                                             PRINTER_ACCESS_USE,
     175             :                                             &pf->handle, &werr);
     176          28 :         if (!NT_STATUS_IS_OK(status)) {
     177           0 :                 goto done;
     178             :         }
     179          28 :         if (!W_ERROR_IS_OK(werr)) {
     180           0 :                 status = werror_to_ntstatus(werr);
     181           0 :                 goto done;
     182             :         }
     183             : 
     184          28 :         info1 = talloc(tmp_ctx, struct spoolss_DocumentInfo1);
     185          28 :         if (info1 == NULL) {
     186           0 :                 status = NT_STATUS_NO_MEMORY;
     187           0 :                 goto done;
     188             :         }
     189          28 :         info1->document_name = pf->docname;
     190          28 :         info1->output_file = pf->filename;
     191          28 :         info1->datatype = "RAW";
     192             : 
     193          28 :         info_ctr.level = 1;
     194          28 :         info_ctr.info.info1 = info1;
     195             : 
     196          28 :         status = dcerpc_spoolss_StartDocPrinter(b, tmp_ctx,
     197             :                                                 &pf->handle,
     198             :                                                 &info_ctr,
     199             :                                                 &pf->jobid,
     200             :                                                 &werr);
     201          28 :         if (!NT_STATUS_IS_OK(status)) {
     202           0 :                 goto done;
     203             :         }
     204          28 :         if (!W_ERROR_IS_OK(werr)) {
     205           0 :                 status = werror_to_ntstatus(werr);
     206           0 :                 goto done;
     207             :         }
     208             : 
     209             :         /* Convert to RAP id. */
     210          28 :         pf->rap_jobid = pjobid_to_rap(pf->svcname, pf->jobid);
     211          28 :         if (pf->rap_jobid == 0) {
     212             :                 /* No errno around here */
     213           0 :                 status = NT_STATUS_ACCESS_DENIED;
     214           0 :                 goto done;
     215             :         }
     216             : 
     217             :         /* setup a full fsp */
     218          56 :         fsp->fsp_name = synthetic_smb_fname(fsp,
     219          28 :                                             pf->filename,
     220             :                                             NULL,
     221             :                                             NULL,
     222             :                                             0,
     223             :                                             0);
     224          28 :         if (fsp->fsp_name == NULL) {
     225           0 :                 status = NT_STATUS_NO_MEMORY;
     226           0 :                 goto done;
     227             :         }
     228             : 
     229          28 :         if (sys_fstat(fd, &fsp->fsp_name->st, false) != 0) {
     230           0 :                 status = map_nt_error_from_unix(errno);
     231           0 :                 goto done;
     232             :         }
     233             : 
     234          28 :         fsp->file_id = vfs_file_id_from_sbuf(fsp->conn, &fsp->fsp_name->st);
     235          28 :         fsp_set_fd(fsp, fd);
     236             : 
     237          28 :         fsp->vuid = current_vuid;
     238          28 :         fsp->fsp_flags.can_lock = false;
     239          28 :         fsp->fsp_flags.can_read = false;
     240          28 :         fsp->access_mask = FILE_GENERIC_WRITE;
     241          28 :         fsp->fsp_flags.can_write = true;
     242          28 :         fsp->fsp_flags.modified = false;
     243          28 :         fsp->oplock_type = NO_OPLOCK;
     244          28 :         fsp->sent_oplock_break = NO_BREAK_SENT;
     245          28 :         fsp->fsp_flags.is_directory = false;
     246          28 :         fsp->fsp_flags.delete_on_close = false;
     247          28 :         fsp->fsp_flags.is_fsa = true;
     248             : 
     249          28 :         fsp->print_file = pf;
     250             : 
     251          28 :         status = NT_STATUS_OK;
     252          28 : done:
     253          28 :         if (!NT_STATUS_IS_OK(status)) {
     254           0 :                 if (fd != -1) {
     255           0 :                         close(fd);
     256           0 :                         if (fsp->print_file) {
     257           0 :                                 unlink(fsp->print_file->filename);
     258             :                         }
     259             :                 }
     260             :                 /* We need to delete the job from spoolss too */
     261           0 :                 if (pf && pf->jobid) {
     262           0 :                         print_spool_terminate(fsp->conn, pf);
     263             :                 }
     264             :         }
     265          28 :         talloc_free(tmp_ctx);
     266          28 :         return status;
     267             : }
     268             : 
     269           0 : int print_spool_write(files_struct *fsp,
     270             :                       const char *data, uint32_t size,
     271             :                       off_t offset, uint32_t *written)
     272             : {
     273           0 :         SMB_STRUCT_STAT st;
     274           0 :         ssize_t n;
     275           0 :         int ret;
     276             : 
     277           0 :         *written = 0;
     278             : 
     279             :         /* first of all stat file to find out if it is still there.
     280             :          * spoolss may have deleted it to signal someone has killed
     281             :          * the job through it's interface */
     282             : 
     283           0 :         if (sys_fstat(fsp_get_io_fd(fsp), &st, false) != 0) {
     284           0 :                 ret = errno;
     285           0 :                 DEBUG(3, ("printfile_offset: sys_fstat failed on %s (%s)\n",
     286             :                           fsp_str_dbg(fsp), strerror(ret)));
     287           0 :                 return ret;
     288             :         }
     289             : 
     290             :         /* check if the file is unlinked, this will signal spoolss has
     291             :          * killed it, just return an error and close the file */
     292           0 :         if (st.st_ex_nlink == 0) {
     293           0 :                 close(fsp_get_io_fd(fsp));
     294           0 :                 return EBADF;
     295             :         }
     296             : 
     297             :         /* When print files go beyond 4GB, the 32-bit offset sent in
     298             :          * old SMBwrite calls is relative to the current 4GB chunk
     299             :          * we're writing to.
     300             :          *    Discovered by Sebastian Kloska <oncaphillis@snafu.de>.
     301             :          */
     302           0 :         if (offset < 0xffffffff00000000LL) {
     303           0 :                 offset = (st.st_ex_size & 0xffffffff00000000LL) + offset;
     304             :         }
     305             : 
     306           0 :         n = write_data_at_offset(fsp_get_io_fd(fsp), data, size, offset);
     307           0 :         if (n == -1) {
     308           0 :                 ret = errno;
     309           0 :                 print_spool_terminate(fsp->conn, fsp->print_file);
     310             :         } else {
     311           0 :                 *written = n;
     312           0 :                 ret = 0;
     313             :         }
     314             : 
     315           0 :         return ret;
     316             : }
     317             : 
     318          28 : void print_spool_end(files_struct *fsp, enum file_close_type close_type)
     319             : {
     320           0 :         NTSTATUS status;
     321           0 :         WERROR werr;
     322          28 :         struct dcerpc_binding_handle *b = NULL;
     323             : 
     324          28 :         if (fsp->fsp_flags.delete_on_close) {
     325           0 :                 int ret;
     326             : 
     327             :                 /*
     328             :                  * Job was requested to be cancelled by setting
     329             :                  * delete on close so truncate the job file.
     330             :                  * print_job_end() which is called from
     331             :                  * _spoolss_EndDocPrinter() will take
     332             :                  * care of deleting it for us.
     333             :                  */
     334           2 :                 ret = ftruncate(fsp_get_io_fd(fsp), 0);
     335           2 :                 if (ret == -1) {
     336           0 :                         DBG_ERR("ftruncate failed: %s\n", strerror(errno));
     337             :                 }
     338             :         }
     339             : 
     340          28 :         b = fsp->conn->spoolss_pipe->binding_handle;
     341             : 
     342          28 :         switch (close_type) {
     343          28 :         case NORMAL_CLOSE:
     344             :         case SHUTDOWN_CLOSE:
     345             :                 /* this also automatically calls spoolss_EndDocPrinter */
     346          28 :                 status = dcerpc_spoolss_ClosePrinter(b, fsp->print_file,
     347          28 :                                                 &fsp->print_file->handle,
     348             :                                                 &werr);
     349          28 :                 if (!NT_STATUS_IS_OK(status) ||
     350          28 :                     !NT_STATUS_IS_OK(status = werror_to_ntstatus(werr))) {
     351           0 :                         DEBUG(3, ("Failed to close printer %s [%s]\n",
     352             :                               fsp->print_file->svcname, nt_errstr(status)));
     353             :                 }
     354          28 :                 break;
     355           0 :         case ERROR_CLOSE:
     356           0 :                 print_spool_terminate(fsp->conn, fsp->print_file);
     357           0 :                 break;
     358             :         }
     359          28 : }
     360             : 
     361             : 
     362           0 : void print_spool_terminate(struct connection_struct *conn,
     363             :                            struct print_file_data *print_file)
     364             : {
     365           0 :         NTSTATUS status;
     366           0 :         WERROR werr;
     367           0 :         struct dcerpc_binding_handle *b = NULL;
     368             : 
     369           0 :         rap_jobid_delete(print_file->svcname, print_file->jobid);
     370             : 
     371           0 :         status = rpc_pipe_open_interface(conn,
     372             :                                          &ndr_table_spoolss,
     373           0 :                                          conn->session_info,
     374           0 :                                          conn->sconn->remote_address,
     375           0 :                                          conn->sconn->local_address,
     376           0 :                                          conn->sconn->msg_ctx,
     377             :                                          &conn->spoolss_pipe);
     378           0 :         if (!NT_STATUS_IS_OK(status)) {
     379           0 :                 DEBUG(0, ("print_spool_terminate: "
     380             :                           "Failed to get spoolss pipe [%s]\n",
     381             :                           nt_errstr(status)));
     382           0 :                 return;
     383             :         }
     384           0 :         b = conn->spoolss_pipe->binding_handle;
     385             : 
     386           0 :         status = dcerpc_spoolss_SetJob(b, print_file,
     387             :                                         &print_file->handle,
     388             :                                         print_file->jobid,
     389             :                                         NULL, SPOOLSS_JOB_CONTROL_DELETE,
     390             :                                         &werr);
     391           0 :         if (!NT_STATUS_IS_OK(status) ||
     392           0 :             !NT_STATUS_IS_OK(status = werror_to_ntstatus(werr))) {
     393           0 :                 DEBUG(3, ("Failed to delete job %d [%s]\n",
     394             :                           print_file->jobid, nt_errstr(status)));
     395           0 :                 return;
     396             :         }
     397           0 :         status = dcerpc_spoolss_ClosePrinter(b, print_file,
     398             :                                              &print_file->handle,
     399             :                                              &werr);
     400           0 :         if (!NT_STATUS_IS_OK(status) ||
     401           0 :             !NT_STATUS_IS_OK(status = werror_to_ntstatus(werr))) {
     402           0 :                 DEBUG(3, ("Failed to close printer %s [%s]\n",
     403             :                           print_file->svcname, nt_errstr(status)));
     404           0 :                 return;
     405             :         }
     406             : }

Generated by: LCOV version 1.14