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 : }
|