2010-04-29 02:55:20 +04:00
/*
Unix SMB / CIFS implementation .
Printing routines that bridge to spoolss
Copyright ( C ) Simo Sorce 2010
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2013-02-25 20:34:21 +04:00
# include "system/filesys.h"
2010-04-29 02:55:20 +04:00
# include "printing.h"
2011-04-13 16:32:16 +04:00
# include "rpc_client/rpc_client.h"
2011-01-15 14:06:09 +03:00
# include "../librpc/gen_ndr/ndr_spoolss_c.h"
2010-07-07 23:24:30 +04:00
# include "rpc_server/rpc_ncacn_np.h"
2010-07-06 19:33:15 +04:00
# include "smbd/globals.h"
2010-10-12 08:27:50 +04:00
# include "../libcli/security/security.h"
2010-04-29 02:55:20 +04:00
2012-06-04 20:15:25 +04:00
struct print_file_data {
char * svcname ;
char * docname ;
char * filename ;
struct policy_handle handle ;
uint32_t jobid ;
2015-05-09 22:33:40 +03:00
uint16_t rap_jobid ;
2012-06-04 20:15:25 +04:00
} ;
2012-06-05 10:29:56 +04:00
uint16_t print_spool_rap_jobid ( struct print_file_data * print_file )
{
if ( print_file = = NULL ) {
return 0 ;
}
return print_file - > rap_jobid ;
}
2010-04-29 02:55:20 +04:00
void print_spool_terminate ( struct connection_struct * conn ,
struct print_file_data * print_file ) ;
/***************************************************************************
* Open a Document over spoolss
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define DOCNAME_DEFAULT "Remote Downlevel Document"
NTSTATUS print_spool_open ( files_struct * fsp ,
const char * fname ,
2012-06-05 17:35:11 +04:00
uint64_t current_vuid )
2010-04-29 02:55:20 +04:00
{
NTSTATUS status ;
TALLOC_CTX * tmp_ctx ;
struct print_file_data * pf ;
2011-01-15 14:06:09 +03:00
struct dcerpc_binding_handle * b = NULL ;
2010-04-29 02:55:20 +04:00
struct spoolss_DevmodeContainer devmode_ctr ;
2013-01-19 01:22:13 +04:00
struct spoolss_DocumentInfoCtr info_ctr ;
struct spoolss_DocumentInfo1 * info1 ;
2010-04-29 02:55:20 +04:00
int fd = - 1 ;
WERROR werr ;
2013-02-25 20:34:21 +04:00
mode_t mask ;
2010-04-29 02:55:20 +04:00
tmp_ctx = talloc_new ( fsp ) ;
if ( ! tmp_ctx ) {
return NT_STATUS_NO_MEMORY ;
}
pf = talloc_zero ( fsp , struct print_file_data ) ;
if ( ! pf ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
2012-07-18 09:37:23 +04:00
pf - > svcname = lp_servicename ( pf , SNUM ( fsp - > conn ) ) ;
2010-04-29 02:55:20 +04:00
/* the document name is derived from the file name.
* " Remote Downlevel Document " is added in front to
* mimic what windows does in this case */
pf - > docname = talloc_strdup ( pf , DOCNAME_DEFAULT ) ;
if ( ! pf - > docname ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
if ( fname ) {
const char * p = strrchr ( fname , ' / ' ) ;
if ( ! p ) {
p = fname ;
}
pf - > docname = talloc_asprintf_append ( pf - > docname , " %s " , p ) ;
if ( ! pf - > docname ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
}
2012-02-01 16:21:04 +04:00
/*
* Ok , now we have to open an actual file .
2010-04-29 02:55:20 +04:00
* Here is the reason :
* We want to write the spool job to this file in
* smbd for scalability reason ( and also because
* apparently window printer drivers can seek when
* spooling to a file ) .
* So we first create a file , and then we pass it
* to spoolss in output_file so it can monitor and
* take over once we call EndDocPrinter ( ) .
* Of course we will not start writing until
2012-02-01 16:21:04 +04:00
* StartDocPrinter ( ) actually gives the ok .
* smbd spooler files do not include a print jobid
* path component , as the jobid is only known after
* calling StartDocPrinter ( ) .
*/
2010-04-29 02:55:20 +04:00
2012-02-01 16:21:04 +04:00
pf - > filename = talloc_asprintf ( pf , " %s/%sXXXXXX " ,
2014-02-02 17:04:46 +04:00
lp_path ( talloc_tos ( ) ,
SNUM ( fsp - > conn ) ) ,
2010-04-29 02:55:20 +04:00
PRINT_SPOOL_PREFIX ) ;
if ( ! pf - > filename ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
errno = 0 ;
2013-02-25 20:34:21 +04:00
mask = umask ( S_IRWXO | S_IRWXG ) ;
2010-04-29 02:55:20 +04:00
fd = mkstemp ( pf - > filename ) ;
2013-02-25 20:34:21 +04:00
umask ( mask ) ;
2010-04-29 02:55:20 +04:00
if ( fd = = - 1 ) {
if ( errno = = EACCES ) {
/* Common setup error, force a report. */
DEBUG ( 0 , ( " Insufficient permissions "
" to open spool file %s. \n " ,
pf - > filename ) ) ;
} else {
/* Normal case, report at level 3 and above. */
DEBUG ( 3 , ( " can't open spool file %s, \n " ,
pf - > filename ) ) ;
DEBUGADD ( 3 , ( " errno = %d (%s). \n " ,
errno , strerror ( errno ) ) ) ;
}
status = map_nt_error_from_unix ( errno ) ;
goto done ;
}
/* now open a document over spoolss so that it does
* all printer verification , and eventually assigns
* a job id */
2010-07-06 19:33:15 +04:00
status = rpc_pipe_open_interface ( fsp - > conn ,
2013-05-17 18:44:05 +04:00
& ndr_table_spoolss ,
2011-02-21 12:25:52 +03:00
fsp - > conn - > session_info ,
2011-06-07 19:21:28 +04:00
fsp - > conn - > sconn - > remote_address ,
2017-02-24 03:29:12 +03:00
fsp - > conn - > sconn - > local_address ,
2010-07-06 19:33:15 +04:00
fsp - > conn - > sconn - > msg_ctx ,
& fsp - > conn - > spoolss_pipe ) ;
2010-04-29 02:55:20 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
2011-01-15 14:06:09 +03:00
b = fsp - > conn - > spoolss_pipe - > binding_handle ;
2010-04-29 02:55:20 +04:00
ZERO_STRUCT ( devmode_ctr ) ;
2011-01-15 14:06:09 +03:00
status = dcerpc_spoolss_OpenPrinter ( b , pf , pf - > svcname ,
2010-04-29 02:55:20 +04:00
" RAW " , devmode_ctr ,
2012-10-08 14:32:49 +04:00
PRINTER_ACCESS_USE ,
2010-04-29 02:55:20 +04:00
& pf - > handle , & werr ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
if ( ! W_ERROR_IS_OK ( werr ) ) {
status = werror_to_ntstatus ( werr ) ;
goto done ;
}
2013-01-19 01:22:13 +04:00
info1 = talloc ( tmp_ctx , struct spoolss_DocumentInfo1 ) ;
if ( info1 = = NULL ) {
2010-04-29 02:55:20 +04:00
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
2013-01-19 01:22:13 +04:00
info1 - > document_name = pf - > docname ;
info1 - > output_file = pf - > filename ;
info1 - > datatype = " RAW " ;
info_ctr . level = 1 ;
info_ctr . info . info1 = info1 ;
status = dcerpc_spoolss_StartDocPrinter ( b , tmp_ctx ,
& pf - > handle ,
& info_ctr ,
& pf - > jobid ,
& werr ) ;
2010-04-29 02:55:20 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
if ( ! W_ERROR_IS_OK ( werr ) ) {
status = werror_to_ntstatus ( werr ) ;
goto done ;
}
/* Convert to RAP id. */
pf - > rap_jobid = pjobid_to_rap ( pf - > svcname , pf - > jobid ) ;
if ( pf - > rap_jobid = = 0 ) {
/* No errno around here */
status = NT_STATUS_ACCESS_DENIED ;
goto done ;
}
/* setup a full fsp */
2016-03-19 07:19:38 +03:00
fsp - > fsp_name = synthetic_smb_fname ( fsp , pf - > filename , NULL , NULL , 0 ) ;
2013-04-15 13:51:59 +04:00
if ( fsp - > fsp_name = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
2010-04-29 02:55:20 +04:00
goto done ;
}
if ( sys_fstat ( fd , & fsp - > fsp_name - > st , false ) ! = 0 ) {
status = map_nt_error_from_unix ( errno ) ;
goto done ;
}
fsp - > file_id = vfs_file_id_from_sbuf ( fsp - > conn , & fsp - > fsp_name - > st ) ;
fsp - > fh - > fd = fd ;
fsp - > vuid = current_vuid ;
fsp - > can_lock = false ;
fsp - > can_read = false ;
fsp - > access_mask = FILE_GENERIC_WRITE ;
fsp - > can_write = true ;
fsp - > modified = false ;
fsp - > oplock_type = NO_OPLOCK ;
fsp - > sent_oplock_break = NO_BREAK_SENT ;
fsp - > is_directory = false ;
fsp - > print_file = pf ;
status = NT_STATUS_OK ;
done :
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( fd ! = - 1 ) {
close ( fd ) ;
2010-12-22 13:34:22 +03:00
if ( fsp - > print_file ) {
unlink ( fsp - > print_file - > filename ) ;
}
2010-04-29 02:55:20 +04:00
}
/* We need to delete the job from spoolss too */
2014-02-14 07:27:12 +04:00
if ( pf & & pf - > jobid ) {
2010-04-29 02:55:20 +04:00
print_spool_terminate ( fsp - > conn , pf ) ;
}
}
talloc_free ( tmp_ctx ) ;
return status ;
}
2010-05-04 12:28:48 +04:00
int print_spool_write ( files_struct * fsp ,
const char * data , uint32_t size ,
2012-04-05 08:53:08 +04:00
off_t offset , uint32_t * written )
2010-04-29 02:55:20 +04:00
{
SMB_STRUCT_STAT st ;
ssize_t n ;
int ret ;
* written = 0 ;
/* first of all stat file to find out if it is still there.
* spoolss may have deleted it to signal someone has killed
* the job through it ' s interface */
if ( sys_fstat ( fsp - > fh - > fd , & st , false ) ! = 0 ) {
ret = errno ;
DEBUG ( 3 , ( " printfile_offset: sys_fstat failed on %s (%s) \n " ,
fsp_str_dbg ( fsp ) , strerror ( ret ) ) ) ;
return ret ;
}
/* check if the file is unlinked, this will signal spoolss has
* killed it , just return an error and close the file */
if ( st . st_ex_nlink = = 0 ) {
close ( fsp - > fh - > fd ) ;
return EBADF ;
}
/* When print files go beyond 4GB, the 32-bit offset sent in
* old SMBwrite calls is relative to the current 4 GB chunk
* we ' re writing to .
* Discovered by Sebastian Kloska < oncaphillis @ snafu . de > .
*/
if ( offset < 0xffffffff00000000LL ) {
offset = ( st . st_ex_size & 0xffffffff00000000LL ) + offset ;
}
n = write_data_at_offset ( fsp - > fh - > fd , data , size , offset ) ;
if ( n = = - 1 ) {
ret = errno ;
print_spool_terminate ( fsp - > conn , fsp - > print_file ) ;
} else {
* written = n ;
ret = 0 ;
}
return ret ;
}
void print_spool_end ( files_struct * fsp , enum file_close_type close_type )
{
NTSTATUS status ;
WERROR werr ;
2011-01-15 14:06:09 +03:00
struct dcerpc_binding_handle * b = NULL ;
2010-04-29 02:55:20 +04:00
2011-01-15 14:06:09 +03:00
b = fsp - > conn - > spoolss_pipe - > binding_handle ;
2010-04-29 02:55:20 +04:00
switch ( close_type ) {
case NORMAL_CLOSE :
case SHUTDOWN_CLOSE :
2010-05-04 10:51:12 +04:00
/* this also automatically calls spoolss_EndDocPrinter */
2011-01-15 14:06:09 +03:00
status = dcerpc_spoolss_ClosePrinter ( b , fsp - > print_file ,
2010-04-29 02:55:20 +04:00
& fsp - > print_file - > handle ,
& werr ) ;
if ( ! NT_STATUS_IS_OK ( status ) | |
! NT_STATUS_IS_OK ( status = werror_to_ntstatus ( werr ) ) ) {
DEBUG ( 3 , ( " Failed to close printer %s [%s] \n " ,
fsp - > print_file - > svcname , nt_errstr ( status ) ) ) ;
}
break ;
case ERROR_CLOSE :
print_spool_terminate ( fsp - > conn , fsp - > print_file ) ;
break ;
}
}
void print_spool_terminate ( struct connection_struct * conn ,
struct print_file_data * print_file )
{
NTSTATUS status ;
WERROR werr ;
2011-01-15 14:06:09 +03:00
struct dcerpc_binding_handle * b = NULL ;
2010-04-29 02:55:20 +04:00
rap_jobid_delete ( print_file - > svcname , print_file - > jobid ) ;
2010-07-06 19:33:15 +04:00
status = rpc_pipe_open_interface ( conn ,
2013-05-17 18:44:05 +04:00
& ndr_table_spoolss ,
2011-02-21 12:25:52 +03:00
conn - > session_info ,
2011-06-07 19:21:28 +04:00
conn - > sconn - > remote_address ,
2017-02-24 03:29:12 +03:00
conn - > sconn - > local_address ,
2010-07-06 19:33:15 +04:00
conn - > sconn - > msg_ctx ,
& conn - > spoolss_pipe ) ;
2010-04-29 02:55:20 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " print_spool_terminate: "
" Failed to get spoolss pipe [%s] \n " ,
nt_errstr ( status ) ) ) ;
return ;
}
2011-01-15 14:06:09 +03:00
b = conn - > spoolss_pipe - > binding_handle ;
2010-04-29 02:55:20 +04:00
2011-01-15 14:06:09 +03:00
status = dcerpc_spoolss_SetJob ( b , print_file ,
2010-04-29 02:55:20 +04:00
& print_file - > handle ,
print_file - > jobid ,
NULL , SPOOLSS_JOB_CONTROL_DELETE ,
& werr ) ;
if ( ! NT_STATUS_IS_OK ( status ) | |
! NT_STATUS_IS_OK ( status = werror_to_ntstatus ( werr ) ) ) {
DEBUG ( 3 , ( " Failed to delete job %d [%s] \n " ,
print_file - > jobid , nt_errstr ( status ) ) ) ;
return ;
}
2011-01-15 14:06:09 +03:00
status = dcerpc_spoolss_ClosePrinter ( b , print_file ,
2010-04-29 02:55:20 +04:00
& print_file - > handle ,
& werr ) ;
if ( ! NT_STATUS_IS_OK ( status ) | |
! NT_STATUS_IS_OK ( status = werror_to_ntstatus ( werr ) ) ) {
DEBUG ( 3 , ( " Failed to close printer %s [%s] \n " ,
print_file - > svcname , nt_errstr ( status ) ) ) ;
return ;
}
}