diff options
-rw-r--r-- | source3/include/smb.h | 1 | ||||
-rw-r--r-- | source3/lib/recvfile.c | 40 | ||||
-rw-r--r-- | source3/lib/util_sock.c | 106 | ||||
-rw-r--r-- | source3/param/loadparm.c | 5 | ||||
-rw-r--r-- | source3/smbd/blocking.c | 4 | ||||
-rw-r--r-- | source3/smbd/fileio.c | 41 | ||||
-rw-r--r-- | source3/smbd/notify.c | 2 | ||||
-rw-r--r-- | source3/smbd/process.c | 62 | ||||
-rw-r--r-- | source3/smbd/reply.c | 95 | ||||
-rw-r--r-- | source3/smbd/server.c | 299 | ||||
-rw-r--r-- | source3/smbd/vfs.c | 32 |
11 files changed, 527 insertions, 160 deletions
diff --git a/source3/include/smb.h b/source3/include/smb.h index 4c51acf6f4..303f7606d3 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -675,6 +675,7 @@ struct smb_request { uint8 wct; const uint8 *inbuf; uint8 *outbuf; + size_t unread_bytes; }; /* Defines for the sent_oplock_break field above. */ diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c index cd9fb12716..9d77f94a29 100644 --- a/source3/lib/recvfile.c +++ b/source3/lib/recvfile.c @@ -125,13 +125,49 @@ static ssize_t default_sys_recvfile(int fromfd, } #if defined(HAVE_SPLICE_SYSCALL) + +#ifdef JRA_SPLICE_TEST +#include <linux/unistd.h> +#include <sys/syscall.h> + +#define __NR_splice 313 +_syscall6( long, splice, + int, fromfd, + loff_t *, fromoffset, + int, tofd, + loff_t *, tooffset, + size_t, count, + unsigned int, flags); +#endif + ssize_t sys_recvfile(int fromfd, int tofd, SMB_OFF_T offset, size_t count) { - errno = ENOSYS - return -1; + size_t total = 0; + + if (count == 0) { + return 0; + } + + while (total < count) { + ssize_t ret = splice(fromfd, + NULL, + tofd, + &offset, + count, + 0); + if (ret == -1) { + if (errno != EINTR) { + return -1; + } + continue; + } + total += ret; + count -= ret; + } + return total; } #else diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c index e66bd5f15a..bbcbcacb4a 100644 --- a/source3/lib/util_sock.c +++ b/source3/lib/util_sock.c @@ -1113,7 +1113,7 @@ bool send_keepalive(int client) Timeout is in milliseconds. ****************************************************************************/ -static ssize_t read_smb_length_return_keepalive(int fd, +ssize_t read_smb_length_return_keepalive(int fd, char *inbuf, unsigned int timeout) { @@ -1260,86 +1260,6 @@ ssize_t receive_smb_raw(int fd, return len; } -static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd, - char **buffer, unsigned int timeout) -{ - char lenbuf[4]; - ssize_t len,ret; - - smb_read_error = 0; - - len = read_smb_length_return_keepalive(fd, lenbuf, timeout); - if (len < 0) { - DEBUG(10,("receive_smb_raw: length < 0!\n")); - - /* - * Correct fix. smb_read_error may have already been - * set. Only set it here if not already set. Global - * variables still suck :-). JRA. - */ - - if (smb_read_error == 0) - smb_read_error = READ_ERROR; - return -1; - } - - /* - * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes - * of header. Don't print the error if this fits.... JRA. - */ - - if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) { - DEBUG(0,("Invalid packet length! (%lu bytes).\n", - (unsigned long)len)); - if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) { - - /* - * Correct fix. smb_read_error may have already been - * set. Only set it here if not already set. Global - * variables still suck :-). JRA. - */ - - if (smb_read_error == 0) - smb_read_error = READ_ERROR; - return -1; - } - } - - /* - * The +4 here can't wrap, we've checked the length above already. - */ - - *buffer = TALLOC_ARRAY(mem_ctx, char, len+4); - - if (*buffer == NULL) { - DEBUG(0, ("Could not allocate inbuf of length %d\n", - (int)len+4)); - if (smb_read_error == 0) - smb_read_error = READ_ERROR; - return -1; - } - - memcpy(*buffer, lenbuf, sizeof(lenbuf)); - - if(len > 0) { - if (timeout > 0) { - ret = read_socket_with_timeout(fd,(*buffer)+4, len, - len, timeout); - } else { - ret = read_data(fd, (*buffer)+4, len); - } - - if (ret != len) { - if (smb_read_error == 0) { - smb_read_error = READ_ERROR; - } - return -1; - } - } - - return len + 4; -} - /**************************************************************************** Wrapper for receive_smb_raw(). Checks the MAC on signed packets. @@ -1364,30 +1284,6 @@ bool receive_smb(int fd, char *buffer, unsigned int timeout) return true; } -ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer, - unsigned int timeout) -{ - ssize_t len; - - len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout); - - if (len < 0) { - return -1; - } - - /* Check the incoming SMB signature. */ - if (!srv_check_sign_mac(*buffer, true)) { - DEBUG(0, ("receive_smb: SMB Signature verification failed on " - "incoming packet!\n")); - if (smb_read_error == 0) { - smb_read_error = READ_BAD_SIG; - } - return -1; - } - - return len; -} - /**************************************************************************** Send an smb to a fd. ****************************************************************************/ diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index dcec6bce89..a5b2647567 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -331,6 +331,7 @@ typedef struct { bool bResetOnZeroVC; int iKeepalive; + int iminreceivefile; param_opt_struct *param_opt; } global; @@ -998,6 +999,7 @@ static struct parm_struct parm_table[] = { {"max protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_ADVANCED}, {"protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_ADVANCED}, {"min protocol", P_ENUM, P_GLOBAL, &Globals.minprotocol, NULL, enum_protocol, FLAG_ADVANCED}, + {"min receivefile size", P_INTEGER, P_GLOBAL, &Globals.iminreceivefile, NULL, NULL, FLAG_ADVANCED}, {"read raw", P_BOOL, P_GLOBAL, &Globals.bReadRaw, NULL, NULL, FLAG_ADVANCED}, {"write raw", P_BOOL, P_GLOBAL, &Globals.bWriteRaw, NULL, NULL, FLAG_ADVANCED}, {"disable netbios", P_BOOL, P_GLOBAL, &Globals.bDisableNetbios, NULL, NULL, FLAG_ADVANCED}, @@ -1708,6 +1710,8 @@ static void init_globals(bool first_time_only) /* By default no shares out of the registry */ Globals.bRegistryShares = False; + + Globals.iminreceivefile = 0; } /******************************************************************* @@ -2165,6 +2169,7 @@ FN_GLOBAL_INTEGER(lp_algorithmic_rid_base, &Globals.AlgorithmicRidBase) FN_GLOBAL_INTEGER(lp_name_cache_timeout, &Globals.name_cache_timeout) FN_GLOBAL_INTEGER(lp_client_signing, &Globals.client_signing) FN_GLOBAL_INTEGER(lp_server_signing, &Globals.server_signing) +FN_GLOBAL_INTEGER(lp_min_receive_file_size, &Globals.iminreceivefile); FN_GLOBAL_INTEGER(lp_client_ldap_sasl_wrapping, &Globals.client_ldap_sasl_wrapping) /* local prototypes */ diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index 66a61449a1..0078bb7d13 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -259,7 +259,7 @@ static void reply_lockingX_success(blocking_lock_record *blr) smb_panic("Could not allocate smb_request"); } - init_smb_request(req, (uint8 *)blr->inbuf); + init_smb_request(req, (uint8 *)blr->inbuf, 0); reply_outbuf(req, 2, 0); /* @@ -531,7 +531,7 @@ static bool process_trans2(blocking_lock_record *blr) return True; } - init_smb_request(req, (uint8 *)blr->inbuf); + init_smb_request(req, (uint8 *)blr->inbuf, 0); SCVAL(req->inbuf, smb_com, SMBtrans2); SSVAL(params,0,0); diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c index 14056ddc80..9e296f2204 100644 --- a/source3/smbd/fileio.c +++ b/source3/smbd/fileio.c @@ -116,12 +116,16 @@ static unsigned int allocated_write_caches; *Really* write to a file. ****************************************************************************/ -static ssize_t real_write_file(files_struct *fsp,const char *data, SMB_OFF_T pos, size_t n) +static ssize_t real_write_file(struct smb_request *req, + files_struct *fsp, + const char *data, + SMB_OFF_T pos, + size_t n) { ssize_t ret; if (pos == -1) { - ret = vfs_write_data(fsp, data, n); + ret = vfs_write_data(req, fsp, data, n); } else { fsp->fh->pos = pos; if (pos && lp_strict_allocate(SNUM(fsp->conn))) { @@ -129,7 +133,7 @@ static ssize_t real_write_file(files_struct *fsp,const char *data, SMB_OFF_T pos return -1; } } - ret = vfs_pwrite_data(fsp, data, n, pos); + ret = vfs_pwrite_data(req, fsp, data, n, pos); } DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n", @@ -191,11 +195,15 @@ static int wcp_file_size_change(files_struct *fsp) Write to a file. ****************************************************************************/ -ssize_t write_file(files_struct *fsp, const char *data, SMB_OFF_T pos, size_t n) +ssize_t write_file(struct smb_request *req, + files_struct *fsp, + const char *data, + SMB_OFF_T pos, + size_t n) { write_cache *wcp = fsp->wcp; ssize_t total_written = 0; - int write_path = -1; + int write_path = -1; if (fsp->print_file) { fstring sharename; @@ -234,8 +242,8 @@ ssize_t write_file(files_struct *fsp, const char *data, SMB_OFF_T pos, size_t n) if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) { setup_write_cache(fsp, st.st_size); wcp = fsp->wcp; - } - } + } + } } #ifdef WITH_PROFILE @@ -280,9 +288,18 @@ nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n", } #endif + if (wcp && req->unread_bytes) { + /* If we're using receivefile don't + * deal with a write cache. + */ + flush_write_cache(fsp, WRITE_FLUSH); + delete_write_cache(fsp); + wcp = NULL; + } + if(!wcp) { DO_PROFILE_INC(writecache_direct_writes); - total_written = real_write_file(fsp, data, pos, n); + total_written = real_write_file(req, fsp, data, pos, n); return total_written; } @@ -291,7 +308,7 @@ nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n", fsp->fh->pos = pos + n; - /* + /* * If we have active cache and it isn't contiguous then we flush. * NOTE: There is a small problem with running out of disk .... */ @@ -610,7 +627,7 @@ len = %u\n",fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (uns if ( n <= wcp->alloc_size && n > wcp->data_size) { cache_flush_needed = True; } else { - ssize_t ret = real_write_file(fsp, data, pos, n); + ssize_t ret = real_write_file(NULL,fsp, data, pos, n); /* * If the write overlaps the entire cache, then @@ -657,7 +674,7 @@ n = %u, wcp->offset=%.0f, wcp->data_size=%u\n", */ if (n > wcp->alloc_size ) { - ssize_t ret = real_write_file(fsp, data, pos, n); + ssize_t ret = real_write_file(NULL,fsp, data, pos, n); if (ret == -1) { return -1; } @@ -828,7 +845,7 @@ ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason) } #endif - ret = real_write_file(fsp, wcp->data, wcp->offset, data_size); + ret = real_write_file(NULL, fsp, wcp->data, wcp->offset, data_size); /* * Ensure file size if kept up to date if write extends file. diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c index ecb7d9dce8..0dd7fbb20e 100644 --- a/source3/smbd/notify.c +++ b/source3/smbd/notify.c @@ -189,7 +189,7 @@ void change_notify_reply(const uint8 *request_buf, uint32 max_param, smb_setlen((char *)tmp_request, smb_size); SCVAL(tmp_request, smb_wct, 0); - init_smb_request(req, tmp_request); + init_smb_request(req, tmp_request,0); send_nt_replies(req, NT_STATUS_OK, prs_data_p(&ps), prs_offset(&ps), NULL, 0); diff --git a/source3/smbd/process.c b/source3/smbd/process.c index ed1bf762e9..1c8d8a6e76 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -25,7 +25,7 @@ extern int smb_echo_count; const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN); -/* +/* * Size of data we can send to client. Set * by the client for all protocols above CORE. * Set by us for CORE protocol. @@ -48,7 +48,9 @@ extern int max_send; * Initialize a struct smb_request from an inbuf */ -void init_smb_request(struct smb_request *req, const uint8 *inbuf) +void init_smb_request(struct smb_request *req, + const uint8 *inbuf, + size_t unread_bytes) { size_t req_size = smb_len(inbuf) + 4; /* Ensure we have at least smb_size bytes. */ @@ -63,6 +65,8 @@ void init_smb_request(struct smb_request *req, const uint8 *inbuf) req->vuid = SVAL(inbuf, smb_uid); req->tid = SVAL(inbuf, smb_tid); req->wct = CVAL(inbuf, smb_wct); + req->unread_bytes = unread_bytes; + /* Ensure we have at least wct words and 2 bytes of bcc. */ if (smb_size + req->wct*2 > req_size) { DEBUG(0,("init_smb_request: invalid wct number %u (size %u)\n", @@ -231,6 +235,14 @@ bool push_deferred_smb_message(struct smb_request *req, { struct timeval end_time; + if (req->unread_bytes) { + DEBUG(0,("push_deferred_smb_message: logic error ! " + "unread_bytes = %u\n", + (unsigned int)req->unread_bytes )); + smb_panic("push_deferred_smb_message: " + "logic error unread_bytes != 0" ); + } + end_time = timeval_sum(&request_time, &timeout); DEBUG(10,("push_deferred_open_smb_message: pushing message len %u mid %u " @@ -382,8 +394,11 @@ static int select_on_fd(int fd, int maxfd, fd_set *fds) The timeout is in milliseconds ****************************************************************************/ -static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer, - size_t *buffer_len, int timeout) +static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, + char **buffer, + size_t *buffer_len, + int timeout, + size_t *p_unread) { fd_set r_fds, w_fds; int selrtn; @@ -391,6 +406,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer, int maxfd = 0; ssize_t len; + *p_unread = 0; smb_read_error = 0; again: @@ -565,7 +581,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer, goto again; } - len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0); + len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0, p_unread); if (len == -1) { return False; @@ -1115,7 +1131,7 @@ static void switch_message(uint8 type, struct smb_request *req, int size) Construct a reply to the incoming packet. ****************************************************************************/ -static void construct_reply(char *inbuf, int size) +static void construct_reply(char *inbuf, int size, size_t unread_bytes) { uint8 type = CVAL(inbuf,smb_com); struct smb_request *req; @@ -1127,10 +1143,19 @@ static void construct_reply(char *inbuf, int size) if (!(req = talloc(talloc_tos(), struct smb_request))) { smb_panic("could not allocate smb_request"); } - init_smb_request(req, (uint8 *)inbuf); + init_smb_request(req, (uint8 *)inbuf, unread_bytes); switch_message(type, req, size); + if (req->unread_bytes) { + /* writeX failed. drain socket. */ + if (drain_socket(smbd_server_fd(), req->unread_bytes) != + req->unread_bytes) { + smb_panic("failed to drain pending bytes"); + } + req->unread_bytes = 0; + } + if (req->outbuf == NULL) { return; } @@ -1152,7 +1177,7 @@ static void construct_reply(char *inbuf, int size) Process an smb from the client ****************************************************************************/ -static void process_smb(char *inbuf, size_t nread) +static void process_smb(char *inbuf, size_t nread, size_t unread_bytes) { static int trans_num; int msg_type = CVAL(inbuf,0); @@ -1176,7 +1201,9 @@ static void process_smb(char *inbuf, size_t nread) DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type, smb_len(inbuf) ) ); - DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, (int)nread ) ); + DEBUG( 3, ( "Transaction %d of length %d (%u toread)\n", trans_num, + (int)nread, + (unsigned int)unread_bytes )); if (msg_type != 0) { /* @@ -1188,8 +1215,8 @@ static void process_smb(char *inbuf, size_t nread) show_msg(inbuf); - construct_reply(inbuf,nread); - + construct_reply(inbuf,nread,unread_bytes); + trans_num++; } @@ -1348,7 +1375,7 @@ void chain_reply(struct smb_request *req) if (!(req2 = talloc(talloc_tos(), struct smb_request))) { smb_panic("could not allocate smb_request"); } - init_smb_request(req2, (uint8 *)inbuf2); + init_smb_request(req2, (uint8 *)inbuf2,0); /* process the request */ switch_message(smb_com2, req2, new_size); @@ -1625,6 +1652,7 @@ void smbd_process(void) { time_t last_timeout_processing_time = time(NULL); unsigned int num_smbs = 0; + size_t unread_bytes = 0; max_recv = MIN(lp_maxxmit(),BUFFER_SIZE); @@ -1635,8 +1663,8 @@ void smbd_process(void) size_t inbuf_len; TALLOC_CTX *frame = talloc_stackframe(); - errno = 0; - + errno = 0; + /* Did someone ask for immediate checks on things like blocking locks ? */ if (select_timeout == 0) { if(!timeout_processing(&select_timeout, @@ -1648,7 +1676,7 @@ void smbd_process(void) run_events(smbd_event_context(), 0, NULL, NULL); while (!receive_message_or_smb(NULL, &inbuf, &inbuf_len, - select_timeout)) { + select_timeout, &unread_bytes)) { if(!timeout_processing(&select_timeout, &last_timeout_processing_time)) return; @@ -1664,10 +1692,10 @@ void smbd_process(void) * faster than the select timeout, thus starving out the * essential processing (change notify, blocking locks) that * the timeout code does. JRA. - */ + */ num_echos = smb_echo_count; - process_smb(inbuf, inbuf_len); + process_smb(inbuf, inbuf_len, unread_bytes); TALLOC_FREE(inbuf); diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 38ce797eeb..4c1ed56d4f 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -3494,7 +3494,7 @@ void reply_writebraw(connection_struct *conn, struct smb_request *req) } if (numtowrite>0) { - nwritten = write_file(fsp,data,startpos,numtowrite); + nwritten = write_file(req,fsp,data,startpos,numtowrite); } DEBUG(3,("reply_writebraw: initial write fnum=%d start=%.0f num=%d " @@ -3572,7 +3572,7 @@ void reply_writebraw(connection_struct *conn, struct smb_request *req) exit_server_cleanly("secondary writebraw failed"); } - nwritten = write_file(fsp,buf+4,startpos+nwritten,numtowrite); + nwritten = write_file(req,fsp,buf+4,startpos+nwritten,numtowrite); if (nwritten == -1) { TALLOC_FREE(buf); reply_unixerror(req, ERRHRD, ERRdiskfull); @@ -3686,7 +3686,7 @@ void reply_writeunlock(connection_struct *conn, struct smb_request *req) if(numtowrite == 0) { nwritten = 0; } else { - nwritten = write_file(fsp,data,startpos,numtowrite); + nwritten = write_file(req,fsp,data,startpos,numtowrite); } status = sync_file(conn, fsp, False /* write through */); @@ -3808,7 +3808,7 @@ void reply_write(connection_struct *conn, struct smb_request *req) return; } } else - nwritten = write_file(fsp,data,startpos,numtowrite); + nwritten = write_file(req,fsp,data,startpos,numtowrite); status = sync_file(conn, fsp, False); if (!NT_STATUS_IS_OK(status)) { @@ -3841,6 +3841,64 @@ void reply_write(connection_struct *conn, struct smb_request *req) } /**************************************************************************** + Ensure a buffer is a valid writeX for recvfile purposes. +****************************************************************************/ + +#define STANDARD_WRITE_AND_X_HEADER_SIZE (smb_size - 4 + /* basic header */ \ + (2*14) + /* word count (including bcc) */ \ + 1 /* pad byte */) + +bool is_valid_writeX_buffer(char *inbuf) +{ + size_t numtowrite; + connection_struct *conn = NULL; + unsigned int doff = 0; + size_t len = smb_len(inbuf); + + if (CVAL(inbuf,smb_com) != SMBwriteX || + CVAL(inbuf,smb_vwv0) != 0xFF || + CVAL(inbuf,smb_wct) != 14) { + return false; + } + conn = conn_find(SVAL(inbuf, smb_tid)); + if (conn == NULL) { + return false; + } + if (IS_IPC(conn)) { + return false; + } + numtowrite = SVAL(inbuf,smb_vwv10); + numtowrite |= ((((size_t)SVAL(inbuf,smb_vwv9)) & 1 )<<16); + if (numtowrite == 0) { + return false; + } + /* Ensure the sizes match up. */ + doff = SVAL(inbuf,smb_vwv11); + + if (doff < STANDARD_WRITE_AND_X_HEADER_SIZE) { + /* no pad byte...old smbclient :-( */ + return false; + } + + if (len - doff != numtowrite) { + DEBUG(10,("is_valid_writeX_buffer: doff mismatch " + "len = %u, doff = %u, numtowrite = %u\n", + (unsigned int)len, + (unsigned int)doff, + (unsigned int)numtowrite )); + return false; + } + + DEBUG(10,("is_valid_writeX_buffer: true " + "len = %u, doff = %u, numtowrite = %u\n", + (unsigned int)len, + (unsigned int)doff, + (unsigned int)numtowrite )); + + return true; +} + +/**************************************************************************** Reply to a write and X. ****************************************************************************/ @@ -3875,10 +3933,18 @@ void reply_write_and_X(connection_struct *conn, struct smb_request *req) numtowrite |= ((((size_t)SVAL(req->inbuf,smb_vwv9)) & 1 )<<16); } - if(smb_doff > smblen || (smb_doff + numtowrite > smblen)) { - reply_doserror(req, ERRDOS, ERRbadmem); - END_PROFILE(SMBwriteX); - return; + if (req->unread_bytes) { + if (numtowrite != req->unread_bytes) { + reply_doserror(req, ERRDOS, ERRbadmem); + END_PROFILE(SMBwriteX); + return; + } + } else { + if (smb_doff > smblen || smb_doff + numtowrite > smblen) { + reply_doserror(req, ERRDOS, ERRbadmem); + END_PROFILE(SMBwriteX); + return; + } } /* If it's an IPC, pass off the pipe handler. */ @@ -3947,15 +4013,16 @@ void reply_write_and_X(connection_struct *conn, struct smb_request *req) nwritten = 0; } else { - if (schedule_aio_write_and_X(conn, req, fsp, data, startpos, - numtowrite)) { + if (req->unread_bytes == 0 && + schedule_aio_write_and_X(conn, req, fsp, data, + startpos, numtowrite)) { END_PROFILE(SMBwriteX); return; } - nwritten = write_file(fsp,data,startpos,numtowrite); + nwritten = write_file(req,fsp,data,startpos,numtowrite); } - + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { reply_unixerror(req, ERRHRD, ERRdiskfull); END_PROFILE(SMBwriteX); @@ -4264,7 +4331,7 @@ void reply_writeclose(connection_struct *conn, struct smb_request *req) return; } - nwritten = write_file(fsp,data,startpos,numtowrite); + nwritten = write_file(req,fsp,data,startpos,numtowrite); set_filetime(conn, fsp->fsp_name, mtime); @@ -4726,7 +4793,7 @@ void reply_printwrite(connection_struct *conn, struct smb_request *req) data = smb_buf(req->inbuf) + 3; - if (write_file(fsp,data,-1,numtowrite) != numtowrite) { + if (write_file(req,fsp,data,-1,numtowrite) != numtowrite) { reply_unixerror(req, ERRHRD, ERRdiskfull); END_PROFILE(SMBsplwr); return; diff --git a/source3/smbd/server.c b/source3/smbd/server.c index b9ab7ef7ac..25e2d2cb5e 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -1,20 +1,22 @@ -/* +/* Unix SMB/CIFS implementation. Main SMB server routines Copyright (C) Andrew Tridgell 1992-1998 Copyright (C) Martin Pool 2002 Copyright (C) Jelmer Vernooij 2002-2003 - + Copyright (C) Volker Lendecke 1993-2007 + Copyright (C) Jeremy Allison 1993-2007 + 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/>. */ @@ -37,6 +39,8 @@ extern SIG_ATOMIC_T got_sig_term; extern SIG_ATOMIC_T reload_after_sighup; static SIG_ATOMIC_T got_sig_cld; +extern int smb_read_error; + #ifdef WITH_DFS extern int dcelogin_atmost_once; #endif /* WITH_DFS */ @@ -60,6 +64,293 @@ static void smbd_set_server_fd(int fd) client_setfd(fd); } +/* Socket functions for smbd packet processing. */ + +static bool valid_packet_size(len) +{ + /* + * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes + * of header. Don't print the error if this fits.... JRA. + */ + + if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) { + DEBUG(0,("Invalid packet length! (%lu bytes).\n", + (unsigned long)len)); + if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) { + + /* + * Correct fix. smb_read_error may have already been + * set. Only set it here if not already set. Global + * variables still suck :-). JRA. + */ + + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return false; + } + } + return true; +} + +static ssize_t read_packet_remainder(int fd, + char *buffer, + unsigned int timeout, + ssize_t len) +{ + ssize_t ret; + + if(len <= 0) { + return len; + } + + if (timeout > 0) { + ret = read_socket_with_timeout(fd, + buffer, + len, + len, + timeout); + } else { + ret = read_data(fd, buffer, len); + } + + if (ret != len) { + if (smb_read_error == 0) { + smb_read_error = READ_ERROR; + } + return -1; + } + + return len; +} + +/**************************************************************************** + Attempt a zerocopy writeX read. We know here that len > smb_size-4 +****************************************************************************/ + +/* + * Unfortunately, earlier versions of smbclient/libsmbclient + * don't send this "standard" writeX header. I've fixed this + * for 3.2 but we'll use the old method with earlier versions. + * Windows and CIFSFS at least use this standard size. Not + * sure about MacOSX. + */ + +#define STANDARD_WRITE_AND_X_HEADER_SIZE (smb_size - 4 + /* basic header */ \ + (2*14) + /* word count (including bcc) */ \ + 1 /* pad byte */) + +ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, + const char lenbuf[4], + int fd, + char **buffer, + unsigned int timeout, + size_t *p_unread) +{ + /* Size of a WRITEX call (+4 byte len). */ + char writeX_header[4 + STANDARD_WRITE_AND_X_HEADER_SIZE]; + ssize_t len = smb_len(lenbuf); + ssize_t toread; + ssize_t ret; + + memcpy(writeX_header, lenbuf, sizeof(lenbuf)); + + if (timeout > 0) { + ret = read_socket_with_timeout(fd, + writeX_header + 4, + STANDARD_WRITE_AND_X_HEADER_SIZE, + STANDARD_WRITE_AND_X_HEADER_SIZE, + timeout); + } else { + ret = read_data(fd, + writeX_header+4, + STANDARD_WRITE_AND_X_HEADER_SIZE); + } + + if (ret != STANDARD_WRITE_AND_X_HEADER_SIZE) { + if (smb_read_error == 0) { + smb_read_error = READ_ERROR; + } + return -1; + } + + /* + * Ok - now try and see if this is a possible + * valid writeX call. + */ + + if (is_valid_writeX_buffer(writeX_header)) { + /* + * If the data offset is beyond what + * we've read, drain the extra bytes. + */ + uint16_t doff = SVAL(writeX_header,smb_vwv11); + ssize_t newlen; + + if (doff > STANDARD_WRITE_AND_X_HEADER_SIZE) { + size_t drain = doff - STANDARD_WRITE_AND_X_HEADER_SIZE; + if (drain_socket(smbd_server_fd(), drain) != drain) { + smb_panic("receive_smb_raw_talloc_partial_read:" + " failed to drain pending bytes"); + } + } else { + doff = STANDARD_WRITE_AND_X_HEADER_SIZE; + } + + /* Spoof down the length and null out the bcc. */ + set_message_bcc(writeX_header, 0); + newlen = smb_len(writeX_header); + + /* Copy the header we've written. */ + + *buffer = TALLOC_MEMDUP(mem_ctx, + writeX_header, + sizeof(writeX_header)); + + if (*buffer == NULL) { + DEBUG(0, ("Could not allocate inbuf of length %d\n", + (int)sizeof(writeX_header))); + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return -1; + } + + /* Work out the remaining bytes. */ + *p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE; + + return newlen + 4; + } + + if (!valid_packet_size(len)) { + return -1; + } + + /* + * Not a valid writeX call. Just do the standard + * talloc and return. + */ + + *buffer = TALLOC_ARRAY(mem_ctx, char, len+4); + + if (*buffer == NULL) { + DEBUG(0, ("Could not allocate inbuf of length %d\n", + (int)len+4)); + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return -1; + } + + /* Copy in what we already read. */ + memcpy(*buffer, + writeX_header, + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE); + toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE; + + if(toread > 0) { + ret = read_packet_remainder(fd, + (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE, + timeout, + toread); + if (ret != toread) { + return -1; + } + } + + return len + 4; +} + +static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, + int fd, + char **buffer, + unsigned int timeout, + size_t *p_unread) +{ + char lenbuf[4]; + ssize_t len,ret; + int min_recv_size = lp_min_receive_file_size(); + + smb_read_error = 0; + *p_unread = 0; + + len = read_smb_length_return_keepalive(fd, lenbuf, timeout); + if (len < 0) { + DEBUG(10,("receive_smb_raw: length < 0!\n")); + + /* + * Correct fix. smb_read_error may have already been + * set. Only set it here if not already set. Global + * variables still suck :-). JRA. + */ + + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return -1; + } + + if (CVAL(lenbuf,0) != SMBkeepalive && + min_recv_size && + len > min_recv_size && + !srv_is_signing_active()) { + + return receive_smb_raw_talloc_partial_read(mem_ctx, + lenbuf, + fd, + buffer, + timeout, + p_unread); + } + + if (!valid_packet_size(len)) { + return -1; + } + + /* + * The +4 here can't wrap, we've checked the length above already. + */ + + *buffer = TALLOC_ARRAY(mem_ctx, char, len+4); + + if (*buffer == NULL) { + DEBUG(0, ("Could not allocate inbuf of length %d\n", + (int)len+4)); + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return -1; + } + + memcpy(*buffer, lenbuf, sizeof(lenbuf)); + + ret = read_packet_remainder(fd, (*buffer)+4, timeout, len); + if (ret != len) { + return -1; + } + + return len + 4; +} + +ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer, + unsigned int timeout, size_t *p_unread) +{ + ssize_t len; + + len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout, p_unread); + + if (len < 0) { + return -1; + } + + /* Check the incoming SMB signature. */ + if (!srv_check_sign_mac(*buffer, true)) { + DEBUG(0, ("receive_smb: SMB Signature verification failed on " + "incoming packet!\n")); + if (smb_read_error == 0) { + smb_read_error = READ_BAD_SIG; + } + return -1; + } + + return len; +} + struct event_context *smbd_event_context(void) { static struct event_context *ctx; diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index e862710b6c..c1c1939153 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -418,11 +418,24 @@ ssize_t vfs_pread_data(files_struct *fsp, char *buf, Write data to a fd on the vfs. ****************************************************************************/ -ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N) +ssize_t vfs_write_data(struct smb_request *req, + files_struct *fsp, + const char *buffer, + size_t N) { size_t total=0; ssize_t ret; + if (req && req->unread_bytes) { + SMB_ASSERT(req->unread_bytes == N); + req->unread_bytes = 0; + return SMB_VFS_RECVFILE(smbd_server_fd(), + fsp, + fsp->fh->fd, + (SMB_OFF_T)-1, + N); + } + while (total < N) { ret = SMB_VFS_WRITE(fsp,fsp->fh->fd,buffer + total,N - total); @@ -436,12 +449,25 @@ ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N) return (ssize_t)total; } -ssize_t vfs_pwrite_data(files_struct *fsp,const char *buffer, - size_t N, SMB_OFF_T offset) +ssize_t vfs_pwrite_data(struct smb_request *req, + files_struct *fsp, + const char *buffer, + size_t N, + SMB_OFF_T offset) { size_t total=0; ssize_t ret; + if (req && req->unread_bytes) { + SMB_ASSERT(req->unread_bytes == N); + req->unread_bytes = 0; + return SMB_VFS_RECVFILE(smbd_server_fd(), + fsp, + fsp->fh->fd, + offset, + N); + } + while (total < N) { ret = SMB_VFS_PWRITE(fsp, fsp->fh->fd, buffer + total, N - total, offset + total); |