From 22dbd67708f1651a2341d70ce576fac360affccf Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 10 Apr 2006 15:33:04 +0000 Subject: r15018: Merge Volker's ipc/trans2/nttrans changes over into 3.0. Also merge the new POSIX lock code - this is not enabled unless -DDEVELOPER is defined. This doesn't yet map onto underlying system POSIX locks. Updates vfs to allow lock queries. Jeremy. (This used to be commit 08e52ead03304ff04229e1bfe544ff40e2564fc7) --- source3/smbd/blocking.c | 277 ++++++++++---- source3/smbd/ipc.c | 452 ++++++++++++++--------- source3/smbd/nttrans.c | 604 +++++++++++++++--------------- source3/smbd/process.c | 31 +- source3/smbd/reply.c | 112 ++++-- source3/smbd/server.c | 3 +- source3/smbd/trans2.c | 963 ++++++++++++++++++++++++++++++++---------------- source3/smbd/vfs-wrap.c | 12 +- source3/smbd/vfs.c | 1 + 9 files changed, 1561 insertions(+), 894 deletions(-) (limited to 'source3/smbd') diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index 805e45f6ea..6b47d0466b 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -35,6 +35,8 @@ typedef struct _blocking_lock_record { SMB_BIG_UINT offset; SMB_BIG_UINT count; uint16 lock_pid; + enum brl_flavour lock_flav; + enum brl_type lock_type; char *inbuf; int length; } blocking_lock_record; @@ -52,25 +54,6 @@ static void free_blocking_lock_record(blocking_lock_record *blr) SAFE_FREE(blr); } -/**************************************************************************** - Get the files_struct given a particular queued SMB. -*****************************************************************************/ - -static files_struct *get_fsp_from_pkt(char *inbuf) -{ - switch(CVAL(inbuf,smb_com)) { - case SMBlock: - case SMBlockread: - return file_fsp(inbuf,smb_vwv0); - case SMBlockingX: - return file_fsp(inbuf,smb_vwv2); - default: - DEBUG(0,("get_fsp_from_pkt: PANIC - unknown type on blocking lock queue - exiting.!\n")); - exit_server("PANIC - unknown type on blocking lock queue"); - } - return NULL; /* Keep compiler happy. */ -} - /**************************************************************************** Determine if this is a secondary element of a chained SMB. **************************************************************************/ @@ -87,12 +70,19 @@ static void received_unlock_msg(int msg_type, struct process_id src, Function to push a blocking lock request onto the lock queue. ****************************************************************************/ -BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout, - int lock_num, uint16 lock_pid, SMB_BIG_UINT offset, SMB_BIG_UINT count) +BOOL push_blocking_lock_request( char *inbuf, int length, + files_struct *fsp, + int lock_timeout, + int lock_num, + uint16 lock_pid, + enum brl_type lock_type, + enum brl_flavour lock_flav, + SMB_BIG_UINT offset, SMB_BIG_UINT count) { static BOOL set_lock_msg; blocking_lock_record *blr, *tmp; BOOL my_lock_ctx = False; + struct byte_range_lock *br_lck = NULL; NTSTATUS status; if(in_chained_smb() ) { @@ -110,6 +100,9 @@ BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout, return False; } + blr->next = NULL; + blr->prev = NULL; + if((blr->inbuf = (char *)SMB_MALLOC(length)) == NULL) { DEBUG(0,("push_blocking_lock_request: Malloc fail (2)!\n" )); SAFE_FREE(blr); @@ -117,19 +110,33 @@ BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout, } blr->com_type = CVAL(inbuf,smb_com); - blr->fsp = get_fsp_from_pkt(inbuf); + blr->fsp = fsp; blr->expire_time = (lock_timeout == -1) ? (time_t)-1 : time(NULL) + (time_t)lock_timeout; blr->lock_num = lock_num; blr->lock_pid = lock_pid; + blr->lock_flav = lock_flav; + blr->lock_type = lock_type; blr->offset = offset; blr->count = count; memcpy(blr->inbuf, inbuf, length); blr->length = length; + br_lck = brl_get_locks(NULL, blr->fsp); + if (!br_lck) { + free_blocking_lock_record(blr); + return False; + } + /* Add a pending lock record for this. */ - status = brl_lock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum, - lock_pid, procid_self(), blr->fsp->conn->cnum, - offset, count, PENDING_LOCK, &my_lock_ctx); + status = brl_lock(br_lck, + lock_pid, + procid_self(), + offset, + count, + PENDING_LOCK, + blr->lock_flav, + &my_lock_ctx); + TALLOC_FREE(br_lck); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("push_blocking_lock_request: failed to add PENDING_LOCK record.\n")); @@ -227,7 +234,6 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status) { char *inbuf = blr->inbuf; files_struct *fsp = blr->fsp; - connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); uint16 num_ulocks = SVAL(inbuf,smb_vwv6); SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT) 0; uint16 lock_pid; @@ -261,7 +267,11 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status) * request would never have been queued. JRA. */ - do_unlock(fsp,conn,lock_pid,count,offset); + do_unlock(fsp, + lock_pid, + count, + offset, + WINDOWS_LOCK); } generic_blocking_lock_error(blr, status); @@ -274,19 +284,41 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status) static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status) { switch(blr->com_type) { +#if 0 + /* We no longer push blocking lock requests for anything but lockingX and trans2. */ case SMBlock: case SMBlockread: generic_blocking_lock_error(blr, status); break; +#endif case SMBlockingX: reply_lockingX_error(blr, status); break; + case SMBtrans2: + case SMBtranss2: + { + char *outbuf = get_OutBuffer(); + char *inbuf = blr->inbuf; + construct_reply_common(inbuf, outbuf); + /* construct_reply_common has done us the favor to pre-fill the + * command field with SMBtranss2 which is wrong :-) + */ + SCVAL(outbuf,smb_com,SMBtrans2); + ERROR_NT(status); + if (!send_smb(smbd_server_fd(),outbuf)) { + exit_server("blocking_lock_reply_error: send_smb failed."); + } + break; + } default: DEBUG(0,("blocking_lock_reply_error: PANIC - unknown type on blocking lock queue - exiting.!\n")); exit_server("PANIC - unknown type on blocking lock queue"); } } +#if 0 +/* We no longer push blocking lock requests for anything but lockingX and trans2. */ + /**************************************************************************** Attempt to finish off getting all pending blocking locks for a lockread call. Returns True if we want to be removed from the list. @@ -302,7 +334,6 @@ static BOOL process_lockread(blocking_lock_record *blr) SMB_BIG_UINT startpos; size_t numtoread; NTSTATUS status; - connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); files_struct *fsp = blr->fsp; BOOL my_lock_ctx = False; @@ -312,7 +343,14 @@ static BOOL process_lockread(blocking_lock_record *blr) numtoread = MIN(BUFFER_SIZE-outsize,numtoread); data = smb_buf(outbuf) + 3; - status = do_lock_spin( fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtoread, startpos, READ_LOCK, &my_lock_ctx); + status = do_lock_spin(fsp, + SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtoread, + startpos, + READ_LOCK, + WINDOWS_LOCK, + &my_lock_ctx); + if (NT_STATUS_V(status)) { if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) && !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) { @@ -371,7 +409,6 @@ static BOOL process_lock(blocking_lock_record *blr) int outsize; SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0; NTSTATUS status; - connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); files_struct *fsp = blr->fsp; BOOL my_lock_ctx = False; @@ -379,7 +416,14 @@ static BOOL process_lock(blocking_lock_record *blr) offset = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3); errno = 0; - status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, &my_lock_ctx); + status = do_lock_spin(fsp, + SVAL(inbuf,smb_pid), + count, + offset, + WRITE_LOCK, + WINDOWS_LOCK, + &my_lock_ctx); + if (NT_STATUS_IS_ERR(status)) { if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) && !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) { @@ -412,6 +456,7 @@ static BOOL process_lock(blocking_lock_record *blr) send_blocking_reply(outbuf,outsize); return True; } +#endif /**************************************************************************** Attempt to finish off getting all pending blocking locks for a lockingX call. @@ -423,7 +468,6 @@ static BOOL process_lockingX(blocking_lock_record *blr) char *inbuf = blr->inbuf; unsigned char locktype = CVAL(inbuf,smb_vwv3); files_struct *fsp = blr->fsp; - connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); uint16 num_ulocks = SVAL(inbuf,smb_vwv6); uint16 num_locks = SVAL(inbuf,smb_vwv7); SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0; @@ -452,9 +496,17 @@ static BOOL process_lockingX(blocking_lock_record *blr) * request would never have been queued. JRA. */ errno = 0; - status = do_lock_spin(fsp,conn,lock_pid,count,offset, - ((locktype & 1) ? READ_LOCK : WRITE_LOCK), &my_lock_ctx); - if (NT_STATUS_IS_ERR(status)) break; + status = do_lock_spin(fsp, + lock_pid, + count, + offset, + ((locktype & 1) ? READ_LOCK : WRITE_LOCK), + WINDOWS_LOCK, + &my_lock_ctx); + + if (NT_STATUS_IS_ERR(status)) { + break; + } } if(blr->lock_num == num_locks) { @@ -490,6 +542,51 @@ Waiting....\n", return False; } +/**************************************************************************** + Attempt to get the posix lock request from a SMBtrans2 call. + Returns True if we want to be removed from the list. +*****************************************************************************/ + +static BOOL process_trans2(blocking_lock_record *blr) +{ + extern int max_send; + char *inbuf = blr->inbuf; + char *outbuf; + BOOL my_lock_ctx = False; + char params[2]; + NTSTATUS status; + + status = do_lock(blr->fsp, + blr->lock_pid, + blr->count, + blr->offset, + blr->lock_type, + blr->lock_flav, + &my_lock_ctx); + + if (!NT_STATUS_IS_OK(status)) { + if (ERROR_WAS_LOCK_DENIED(status)) { + /* Still can't get the lock, just keep waiting. */ + return False; + } + /* + * We have other than a "can't get lock" + * error. Send an error and return True so we get dequeued. + */ + blocking_lock_reply_error(blr, status); + return True; + } + + /* We finally got the lock, return success. */ + outbuf = get_OutBuffer(); + construct_reply_common(inbuf, outbuf); + SCVAL(outbuf,smb_com,SMBtrans2); + SSVAL(params,0,0); + send_trans2_replies(outbuf, max_send, params, 2, NULL, 0); + return True; +} + + /**************************************************************************** Process a blocking lock SMB. Returns True if we want to be removed from the list. @@ -498,12 +595,18 @@ Waiting....\n", static BOOL blocking_lock_record_process(blocking_lock_record *blr) { switch(blr->com_type) { +#if 0 + /* We no longer push blocking lock requests for anything but lockingX and trans2. */ case SMBlock: return process_lock(blr); case SMBlockread: return process_lockread(blr); +#endif case SMBlockingX: return process_lockingX(blr); + case SMBtrans2: + case SMBtranss2: + return process_trans2(blr); default: DEBUG(0,("blocking_lock_record_process: PANIC - unknown type on blocking lock queue - exiting.!\n")); exit_server("PANIC - unknown type on blocking lock queue"); @@ -522,13 +625,21 @@ void remove_pending_lock_requests_by_fid(files_struct *fsp) for(blr = blocking_lock_queue; blr; blr = next) { next = blr->next; if(blr->fsp->fnum == fsp->fnum) { + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); - DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \ + if (br_lck) { + DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); - brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum, - blr->lock_pid, procid_self(), blr->fsp->conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + + } free_blocking_lock_record(blr); } @@ -547,14 +658,22 @@ void remove_pending_lock_requests_by_mid(int mid) next = blr->next; if(SVAL(blr->inbuf,smb_mid) == mid) { files_struct *fsp = blr->fsp; + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); - DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \ + if (br_lck) { + DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } + blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT); - brl_unlock(blr->fsp->dev, blr->fsp->inode, blr->fsp->fnum, - blr->lock_pid, procid_self(), blr->fsp->conn->cnum, - blr->offset, blr->count, True, NULL, NULL); free_blocking_lock_record(blr); } } @@ -635,16 +754,25 @@ void process_blocking_lock_queue(time_t t) fsp->fnum, fsp->fsp_name )); if((blr->expire_time != -1) && (blr->expire_time <= t)) { + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + /* * Lock expired - throw away all previously * obtained locks and return lock error. */ - DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n", - fsp->fnum, fsp->fsp_name )); - brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, procid_self(), conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + if (br_lck) { + DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n", + fsp->fnum, fsp->fsp_name )); + + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT); free_blocking_lock_record(blr); @@ -652,32 +780,48 @@ void process_blocking_lock_queue(time_t t) } if(!change_to_user(conn,vuid)) { - DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n", - vuid )); + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + /* * Remove the entry and return an error to the client. */ - blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); - brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, procid_self(), conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + if (br_lck) { + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } + DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n", + vuid )); + blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); free_blocking_lock_record(blr); continue; } if(!set_current_service(conn,SVAL(blr->inbuf,smb_flg),True)) { - DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) )); + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + /* * Remove the entry and return an error to the client. */ - blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); - brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, procid_self(), conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + if (br_lck) { + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } + DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) )); + blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); free_blocking_lock_record(blr); change_to_root_user(); continue; @@ -690,10 +834,17 @@ void process_blocking_lock_queue(time_t t) */ if(blocking_lock_record_process(blr)) { - - brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - blr->lock_pid, procid_self(), conn->cnum, - blr->offset, blr->count, True, NULL, NULL); + struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + + if (br_lck) { + brl_remove_pending_lock(br_lck, + blr->lock_pid, + procid_self(), + blr->offset, + blr->count, + blr->lock_flav); + TALLOC_FREE(br_lck); + } free_blocking_lock_record(blr); } diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index 427b6ae214..1b5a5f39c7 100644 --- a/source3/smbd/ipc.c +++ b/source3/smbd/ipc.c @@ -355,259 +355,349 @@ static int named_pipe(connection_struct *conn,uint16 vuid, char *outbuf,char *na return 0; } +static NTSTATUS handle_trans(connection_struct *conn, + struct trans_state *state, + char *outbuf, int *outsize) +{ + char *local_machine_name; + int name_offset = 0; + + DEBUG(3,("trans <%s> data=%u params=%u setup=%u\n", + state->name,state->total_data,state->total_param, + state->setup_count)); + + /* + * WinCE wierdness.... + */ + + local_machine_name = talloc_asprintf(state, "\\%s\\", + get_local_machine_name()); + + if (local_machine_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (strnequal(state->name, local_machine_name, + strlen(local_machine_name))) { + name_offset = strlen(local_machine_name)-1; + } + + if (!strnequal(&state->name[name_offset], "\\PIPE", + strlen("\\PIPE"))) { + return NT_STATUS_NOT_SUPPORTED; + } + + name_offset += strlen("\\PIPE"); + + /* Win9x weirdness. When talking to a unicode server Win9x + only sends \PIPE instead of \PIPE\ */ + + if (state->name[name_offset] == '\\') + name_offset++; + + DEBUG(5,("calling named_pipe\n")); + *outsize = named_pipe(conn, state->vuid, outbuf, + state->name+name_offset, + state->setup,state->data, + state->param, + state->setup_count,state->total_data, + state->total_param, + state->max_setup_return, + state->max_data_return, + state->max_param_return); + + if (*outsize == 0) { + return NT_STATUS_NOT_SUPPORTED; + } + + if (state->close_on_completion) + close_cnum(conn,state->vuid); + + return NT_STATUS_OK; +} /**************************************************************************** Reply to a SMBtrans. ****************************************************************************/ -int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int bufsize) +int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, + int size, int bufsize) { - fstring name; - int name_offset = 0; - char *data=NULL,*params=NULL; - uint16 *setup=NULL; int outsize = 0; - uint16 vuid = SVAL(inbuf,smb_uid); - unsigned int tpscnt = SVAL(inbuf,smb_vwv0); - unsigned int tdscnt = SVAL(inbuf,smb_vwv1); - unsigned int mprcnt = SVAL(inbuf,smb_vwv2); - unsigned int mdrcnt = SVAL(inbuf,smb_vwv3); - unsigned int msrcnt = CVAL(inbuf,smb_vwv4); - BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0); - BOOL one_way = BITSETW(inbuf+smb_vwv5,1); - unsigned int pscnt = SVAL(inbuf,smb_vwv9); - unsigned int psoff = SVAL(inbuf,smb_vwv10); - unsigned int dscnt = SVAL(inbuf,smb_vwv11); - unsigned int dsoff = SVAL(inbuf,smb_vwv12); - unsigned int suwcnt = CVAL(inbuf,smb_vwv13); - char *local_machine_name; + unsigned int dsoff = SVAL(inbuf, smb_dsoff); + unsigned int dscnt = SVAL(inbuf, smb_dscnt); + unsigned int psoff = SVAL(inbuf, smb_psoff); + unsigned int pscnt = SVAL(inbuf, smb_pscnt); + struct trans_state *state; + NTSTATUS result; + START_PROFILE(SMBtrans); - memset(name, '\0',sizeof(name)); - srvstr_pull_buf(inbuf, name, smb_buf(inbuf), sizeof(name), STR_TERMINATE); + if (!NT_STATUS_IS_OK(allow_new_trans(conn->pending_trans, + SVAL(inbuf, smb_mid)))) { + DEBUG(2, ("Got invalid trans request: %s\n", + nt_errstr(result))); + END_PROFILE(SMBtrans); + return ERROR_NT(result); + } - if (dscnt > tdscnt || pscnt > tpscnt) + if ((state = TALLOC_P(NULL, struct trans_state)) == NULL) { + DEBUG(0, ("talloc failed\n")); + END_PROFILE(SMBtrans); + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + state->cmd = SMBtrans; + + state->mid = SVAL(inbuf, smb_mid); + state->vuid = SVAL(inbuf, smb_uid); + state->setup_count = CVAL(inbuf, smb_suwcnt); + state->total_param = SVAL(inbuf, smb_tpscnt); + state->param = NULL; + state->total_data = SVAL(inbuf, smb_tdscnt); + state->data = NULL; + state->max_param_return = SVAL(inbuf, smb_mprcnt); + state->max_data_return = SVAL(inbuf, smb_mdrcnt); + state->max_setup_return = CVAL(inbuf, smb_msrcnt); + state->close_on_completion = BITSETW(inbuf+smb_vwv5,0); + state->one_way = BITSETW(inbuf+smb_vwv5,1); + + memset(state->name, '\0',sizeof(state->name)); + srvstr_pull_buf(inbuf, state->name, smb_buf(inbuf), + sizeof(state->name), STR_TERMINATE); + + if ((dscnt > state->total_data) || (pscnt > state->total_param)) goto bad_param; - - if (tdscnt) { - if((data = (char *)SMB_MALLOC(tdscnt)) == NULL) { - DEBUG(0,("reply_trans: data malloc fail for %u bytes !\n", tdscnt)); + + if (state->total_data) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->data = SMB_MALLOC(state->total_data); + if (state->data == NULL) { + DEBUG(0,("reply_trans: data malloc fail for %u " + "bytes !\n", state->total_data)); + TALLOC_FREE(state); END_PROFILE(SMBtrans); return(ERROR_DOS(ERRDOS,ERRnomem)); } if ((dsoff+dscnt < dsoff) || (dsoff+dscnt < dscnt)) goto bad_param; if ((smb_base(inbuf)+dsoff+dscnt > inbuf + size) || - (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf))) + (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf))) goto bad_param; - memcpy(data,smb_base(inbuf)+dsoff,dscnt); + memcpy(state->data,smb_base(inbuf)+dsoff,dscnt); } - if (tpscnt) { - if((params = (char *)SMB_MALLOC(tpscnt)) == NULL) { - DEBUG(0,("reply_trans: param malloc fail for %u bytes !\n", tpscnt)); - SAFE_FREE(data); + if (state->total_param) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->param = SMB_MALLOC(state->total_param); + if (state->param == NULL) { + DEBUG(0,("reply_trans: param malloc fail for %u " + "bytes !\n", state->total_param)); + SAFE_FREE(state->data); + TALLOC_FREE(state); END_PROFILE(SMBtrans); return(ERROR_DOS(ERRDOS,ERRnomem)); } if ((psoff+pscnt < psoff) || (psoff+pscnt < pscnt)) goto bad_param; if ((smb_base(inbuf)+psoff+pscnt > inbuf + size) || - (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf))) + (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf))) goto bad_param; - memcpy(params,smb_base(inbuf)+psoff,pscnt); + memcpy(state->param,smb_base(inbuf)+psoff,pscnt); } - if (suwcnt) { + state->received_data = dscnt; + state->received_param = pscnt; + + if (state->setup_count) { unsigned int i; - if((setup = SMB_MALLOC_ARRAY(uint16,suwcnt)) == NULL) { - DEBUG(0,("reply_trans: setup malloc fail for %u bytes !\n", (unsigned int)(suwcnt * sizeof(uint16)))); - SAFE_FREE(data); - SAFE_FREE(params); + if((state->setup = TALLOC_ARRAY( + state, uint16, state->setup_count)) == NULL) { + DEBUG(0,("reply_trans: setup malloc fail for %u " + "bytes !\n", (unsigned int) + (state->setup_count * sizeof(uint16)))); + TALLOC_FREE(state); END_PROFILE(SMBtrans); return(ERROR_DOS(ERRDOS,ERRnomem)); } - if (inbuf+smb_vwv14+(suwcnt*SIZEOFWORD) > inbuf + size) + if (inbuf+smb_vwv14+(state->setup_count*SIZEOFWORD) > + inbuf + size) goto bad_param; - if ((smb_vwv14+(suwcnt*SIZEOFWORD) < smb_vwv14) || (smb_vwv14+(suwcnt*SIZEOFWORD) < (suwcnt*SIZEOFWORD))) + if ((smb_vwv14+(state->setup_count*SIZEOFWORD) < smb_vwv14) || + (smb_vwv14+(state->setup_count*SIZEOFWORD) < + (state->setup_count*SIZEOFWORD))) goto bad_param; - for (i=0;isetup_count;i++) + state->setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD); } + state->received_param = pscnt; - srv_signing_trans_start(SVAL(inbuf,smb_mid)); + if ((state->received_param == state->total_param) && + (state->received_data == state->total_data)) { - if (pscnt < tpscnt || dscnt < tdscnt) { - /* We need to send an interim response then receive the rest - of the parameter/data bytes */ - outsize = set_message(outbuf,0,0,True); - show_msg(outbuf); - srv_signing_trans_stop(); - if (!send_smb(smbd_server_fd(),outbuf)) - exit_server("reply_trans: send_smb failed."); - } + result = handle_trans(conn, state, outbuf, &outsize); + + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); - /* receive the rest of the trans packet */ - while (pscnt < tpscnt || dscnt < tdscnt) { - BOOL ret; - unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; - - ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); - - /* - * The sequence number for the trans reply is always - * based on the last secondary received. - */ - - srv_signing_trans_start(SVAL(inbuf,smb_mid)); - - if ((ret && (CVAL(inbuf, smb_com) != SMBtranss)) || !ret) { - if(ret) { - DEBUG(0,("reply_trans: Invalid secondary trans packet\n")); - } else { - DEBUG(0,("reply_trans: %s in getting secondary trans response.\n", - (smb_read_error == READ_ERROR) ? "error" : "timeout" )); - } - SAFE_FREE(params); - SAFE_FREE(data); - SAFE_FREE(setup); + if (!NT_STATUS_IS_OK(result)) { END_PROFILE(SMBtrans); - srv_signing_trans_stop(); - return(ERROR_DOS(ERRSRV,ERRerror)); + return ERROR_NT(result); } - show_msg(inbuf); - - /* Revise total_params and total_data in case they have changed downwards */ - if (SVAL(inbuf,smb_vwv0) < tpscnt) - tpscnt = SVAL(inbuf,smb_vwv0); - if (SVAL(inbuf,smb_vwv1) < tdscnt) - tdscnt = SVAL(inbuf,smb_vwv1); - - pcnt = SVAL(inbuf,smb_vwv2); - poff = SVAL(inbuf,smb_vwv3); - pdisp = SVAL(inbuf,smb_vwv4); - - dcnt = SVAL(inbuf,smb_vwv5); - doff = SVAL(inbuf,smb_vwv6); - ddisp = SVAL(inbuf,smb_vwv7); - - pscnt += pcnt; - dscnt += dcnt; - - if (dscnt > tdscnt || pscnt > tpscnt) - goto bad_param; - - if (pcnt) { - if (pdisp+pcnt > tpscnt) - goto bad_param; - if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt)) - goto bad_param; - if (pdisp > tpscnt) - goto bad_param; - if ((smb_base(inbuf) + poff + pcnt > inbuf + bufsize) || - (smb_base(inbuf) + poff + pcnt < smb_base(inbuf))) - goto bad_param; - if (params + pdisp < params) - goto bad_param; - - memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt); + if (outsize == 0) { + END_PROFILE(SMBtrans); + return ERROR_NT(NT_STATUS_INTERNAL_ERROR); } - if (dcnt) { - if (ddisp+dcnt > tdscnt) - goto bad_param; - if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt)) - goto bad_param; - if (ddisp > tdscnt) - goto bad_param; - if ((smb_base(inbuf) + doff + dcnt > inbuf + bufsize) || - (smb_base(inbuf) + doff + dcnt < smb_base(inbuf))) - goto bad_param; - if (data + ddisp < data) - goto bad_param; - - memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt); - } + END_PROFILE(SMBtrans); + return outsize; } - DEBUG(3,("trans <%s> data=%u params=%u setup=%u\n", - name,tdscnt,tpscnt,suwcnt)); + DLIST_ADD(conn->pending_trans, state); - /* - * WinCE wierdness.... - */ + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + show_msg(outbuf); + END_PROFILE(SMBtrans); + return outsize; - asprintf(&local_machine_name, "\\%s\\", get_local_machine_name()); + bad_param: - if (local_machine_name == NULL) { - srv_signing_trans_stop(); - SAFE_FREE(data); - SAFE_FREE(params); - SAFE_FREE(setup); - END_PROFILE(SMBtrans); - return ERROR_NT(NT_STATUS_NO_MEMORY); + DEBUG(0,("reply_trans: invalid trans parameters\n")); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); +} + +/**************************************************************************** + Reply to a secondary SMBtrans. + ****************************************************************************/ + +int reply_transs(connection_struct *conn, char *inbuf,char *outbuf, + int size, int bufsize) +{ + int outsize = 0; + unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; + struct trans_state *state; + NTSTATUS result; + + START_PROFILE(SMBtranss); + + show_msg(inbuf); + + for (state = conn->pending_trans; state != NULL; + state = state->next) { + if (state->mid == SVAL(inbuf,smb_mid)) { + break; + } } - if (strnequal(name, local_machine_name, strlen(local_machine_name))) { - name_offset = strlen(local_machine_name)-1; + if ((state == NULL) || (state->cmd != SMBtrans)) { + END_PROFILE(SMBtranss); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } - SAFE_FREE(local_machine_name); + /* Revise total_params and total_data in case they have changed + * downwards */ - if (strnequal(&name[name_offset], "\\PIPE", strlen("\\PIPE"))) { - name_offset += strlen("\\PIPE"); + if (SVAL(inbuf, smb_vwv0) < state->total_param) + state->total_param = SVAL(inbuf,smb_vwv0); + if (SVAL(inbuf, smb_vwv1) < state->total_data) + state->total_data = SVAL(inbuf,smb_vwv1); - /* Win9x weirdness. When talking to a unicode server Win9x - only sends \PIPE instead of \PIPE\ */ + pcnt = SVAL(inbuf, smb_spscnt); + poff = SVAL(inbuf, smb_spsoff); + pdisp = SVAL(inbuf, smb_spsdisp); - if (name[name_offset] == '\\') - name_offset++; + dcnt = SVAL(inbuf, smb_sdscnt); + doff = SVAL(inbuf, smb_sdsoff); + ddisp = SVAL(inbuf, smb_sdsdisp); - DEBUG(5,("calling named_pipe\n")); - outsize = named_pipe(conn,vuid,outbuf, - name+name_offset,setup,data,params, - suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt); - } else { - DEBUG(3,("invalid pipe name\n")); - outsize = 0; + state->received_param += pcnt; + state->received_data += dcnt; + + if ((state->received_data > state->total_data) || + (state->received_param > state->total_param)) + goto bad_param; + + if (pcnt) { + if (pdisp+pcnt > state->total_param) + goto bad_param; + if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt)) + goto bad_param; + if (pdisp > state->total_param) + goto bad_param; + if ((smb_base(inbuf) + poff + pcnt > inbuf + size) || + (smb_base(inbuf) + poff + pcnt < smb_base(inbuf))) + goto bad_param; + if (state->param + pdisp < state->param) + goto bad_param; + + memcpy(state->param+pdisp,smb_base(inbuf)+poff, + pcnt); } - - SAFE_FREE(data); - SAFE_FREE(params); - SAFE_FREE(setup); - - srv_signing_trans_stop(); + if (dcnt) { + if (ddisp+dcnt > state->total_data) + goto bad_param; + if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt)) + goto bad_param; + if (ddisp > state->total_data) + goto bad_param; + if ((smb_base(inbuf) + doff + dcnt > inbuf + size) || + (smb_base(inbuf) + doff + dcnt < smb_base(inbuf))) + goto bad_param; + if (state->data + ddisp < state->data) + goto bad_param; - if (close_on_completion) - close_cnum(conn,vuid); + memcpy(state->data+ddisp, smb_base(inbuf)+doff, + dcnt); + } - if (one_way) { - END_PROFILE(SMBtrans); - return(-1); + if ((state->received_param < state->total_param) || + (state->received_data < state->total_data)) { + END_PROFILE(SMBtranss); + return -1; } - - if (outsize == 0) { - END_PROFILE(SMBtrans); + + /* construct_reply_common has done us the favor to pre-fill the + * command field with SMBtranss which is wrong :-) + */ + SCVAL(outbuf,smb_com,SMBtrans); + + result = handle_trans(conn, state, outbuf, &outsize); + + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + + if ((outsize == 0) || !NT_STATUS_IS_OK(result)) { + END_PROFILE(SMBtranss); return(ERROR_DOS(ERRSRV,ERRnosupport)); } - END_PROFILE(SMBtrans); + END_PROFILE(SMBtranss); return(outsize); - bad_param: - srv_signing_trans_stop(); - DEBUG(0,("reply_trans: invalid trans parameters\n")); - SAFE_FREE(data); - SAFE_FREE(params); - SAFE_FREE(setup); - END_PROFILE(SMBtrans); + DEBUG(0,("reply_transs: invalid trans parameters\n")); + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtranss); return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 796eb44332..24d64ecfc7 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -1811,19 +1811,6 @@ int reply_ntrename(connection_struct *conn, return(outsize); } -/**************************************************************************** - Reply to an unsolicited SMBNTtranss - just ignore it! -****************************************************************************/ - -int reply_nttranss(connection_struct *conn, - char *inbuf,char *outbuf,int length,int bufsize) -{ - START_PROFILE(SMBnttranss); - DEBUG(4,("Ignoring nttranss of length %d\n",length)); - END_PROFILE(SMBnttranss); - return(-1); -} - /**************************************************************************** Reply to a notify change - queue the request and don't allow a directory to be opened. @@ -2719,29 +2706,120 @@ static int call_nt_transact_set_user_quota(connection_struct *conn, char *inbuf, } #endif /* HAVE_SYS_QUOTAS */ +static int handle_nttrans(connection_struct *conn, + struct trans_state *state, + char *inbuf, char *outbuf, int size, int bufsize) +{ + int outsize; + + if (Protocol >= PROTOCOL_NT1) { + SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | 0x40); /* IS_LONG_NAME */ + } + + /* Now we must call the relevant NT_TRANS function */ + switch(state->call) { + case NT_TRANSACT_CREATE: + START_PROFILE_NESTED(NT_transact_create); + outsize = call_nt_transact_create(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE_NESTED(NT_transact_create); + break; + case NT_TRANSACT_IOCTL: + START_PROFILE_NESTED(NT_transact_ioctl); + outsize = call_nt_transact_ioctl(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_ioctl); + break; + case NT_TRANSACT_SET_SECURITY_DESC: + START_PROFILE_NESTED(NT_transact_set_security_desc); + outsize = call_nt_transact_set_security_desc(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_set_security_desc); + break; + case NT_TRANSACT_NOTIFY_CHANGE: + START_PROFILE_NESTED(NT_transact_notify_change); + outsize = call_nt_transact_notify_change(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_notify_change); + break; + case NT_TRANSACT_RENAME: + START_PROFILE_NESTED(NT_transact_rename); + outsize = call_nt_transact_rename(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_rename); + break; + + case NT_TRANSACT_QUERY_SECURITY_DESC: + START_PROFILE_NESTED(NT_transact_query_security_desc); + outsize = call_nt_transact_query_security_desc(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_query_security_desc); + break; +#ifdef HAVE_SYS_QUOTAS + case NT_TRANSACT_GET_USER_QUOTA: + START_PROFILE_NESTED(NT_transact_get_user_quota); + outsize = call_nt_transact_get_user_quota(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_get_user_quota); + break; + case NT_TRANSACT_SET_USER_QUOTA: + START_PROFILE_NESTED(NT_transact_set_user_quota); + outsize = call_nt_transact_set_user_quota(conn, inbuf, outbuf, + size, bufsize, + (char **)&state->setup, state->setup_count, + &state->param, state->total_param, + &state->data, state->total_data, state->max_data_return); + END_PROFILE_NESTED(NT_transact_set_user_quota); + break; +#endif /* HAVE_SYS_QUOTAS */ + default: + /* Error in request */ + DEBUG(0,("reply_nttrans: Unknown request %d in nttrans call\n", + state->call)); + return ERROR_DOS(ERRSRV,ERRerror); + } + return outsize; +} + /**************************************************************************** Reply to a SMBNTtrans. ****************************************************************************/ int reply_nttrans(connection_struct *conn, - char *inbuf,char *outbuf,int length,int bufsize) + char *inbuf,char *outbuf,int size,int bufsize) { int outsize = 0; - uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount); -#if 0 /* Not used. */ - uint16 max_setup_count = CVAL(inbuf, smb_nt_MaxSetupCount); - uint32 max_parameter_count = IVAL(inbuf, smb_nt_MaxParameterCount); -#endif /* Not used. */ - uint32 total_parameter_count = IVAL(inbuf, smb_nt_TotalParameterCount); - uint32 total_data_count = IVAL(inbuf, smb_nt_TotalDataCount); - uint32 parameter_count = IVAL(inbuf,smb_nt_ParameterCount); - uint32 parameter_offset = IVAL(inbuf,smb_nt_ParameterOffset); - uint32 data_count = IVAL(inbuf,smb_nt_DataCount); - uint32 data_offset = IVAL(inbuf,smb_nt_DataOffset); - uint16 setup_count = 2*CVAL(inbuf,smb_nt_SetupCount); /* setup count is in *words* */ + uint32 pscnt = IVAL(inbuf,smb_nt_ParameterCount); + uint32 psoff = IVAL(inbuf,smb_nt_ParameterOffset); + uint32 dscnt = IVAL(inbuf,smb_nt_DataCount); + uint32 dsoff = IVAL(inbuf,smb_nt_DataOffset); + uint16 function_code = SVAL( inbuf, smb_nt_Function); - char *params = NULL, *data = NULL, *setup = NULL; - uint32 num_params_sofar, num_data_sofar; + NTSTATUS result; + struct trans_state *state; + START_PROFILE(SMBnttrans); if (IS_IPC(conn) && (function_code != NT_TRANSACT_CREATE)) { @@ -2749,319 +2827,269 @@ int reply_nttrans(connection_struct *conn, return ERROR_DOS(ERRSRV,ERRaccess); } - outsize = set_message(outbuf,0,0,True); + if (!NT_STATUS_IS_OK(allow_new_trans(conn->pending_trans, + SVAL(inbuf, smb_mid)))) { + DEBUG(2, ("Got invalid nttrans request: %s\n", nt_errstr(result))); + END_PROFILE(SMBnttrans); + return ERROR_NT(result); + } + + if ((state = TALLOC_P(NULL, struct trans_state)) == NULL) { + END_PROFILE(SMBnttrans); + return ERROR_DOS(ERRSRV,ERRaccess); + } + + state->cmd = SMBnttrans; + + state->mid = SVAL(inbuf,smb_mid); + state->vuid = SVAL(inbuf,smb_uid); + state->total_data = IVAL(inbuf, smb_nt_TotalDataCount); + state->data = NULL; + state->total_param = IVAL(inbuf, smb_nt_TotalParameterCount); + state->param = NULL; + state->max_data_return = IVAL(inbuf,smb_nt_MaxDataCount); + + /* setup count is in *words* */ + state->setup_count = 2*CVAL(inbuf,smb_nt_SetupCount); + state->call = function_code; /* - * All nttrans messages we handle have smb_wct == 19 + setup_count. - * Ensure this is so as a sanity check. + * All nttrans messages we handle have smb_wct == 19 + + * state->setup_count. Ensure this is so as a sanity check. */ - if(CVAL(inbuf, smb_wct) != 19 + (setup_count/2)) { + if(CVAL(inbuf, smb_wct) != 19 + (state->setup_count/2)) { DEBUG(2,("Invalid smb_wct %d in nttrans call (should be %d)\n", - CVAL(inbuf, smb_wct), 19 + (setup_count/2))); + CVAL(inbuf, smb_wct), 19 + (state->setup_count/2))); goto bad_param; } /* Don't allow more than 128mb for each value. */ - if ((total_parameter_count > (1024*1024*128)) || (total_data_count > (1024*1024*128))) { + if ((state->total_data > (1024*1024*128)) || + (state->total_param > (1024*1024*128))) { END_PROFILE(SMBnttrans); return ERROR_DOS(ERRDOS,ERRnomem); } - /* Allocate the space for the setup, the maximum needed parameters and data */ - - if(setup_count > 0) { - setup = (char *)SMB_MALLOC(setup_count); - } - if (total_parameter_count > 0) { - params = (char *)SMB_MALLOC(total_parameter_count); - } - if (total_data_count > 0) { - data = (char *)SMB_MALLOC(total_data_count); - } - - if ((total_parameter_count && !params) || (total_data_count && !data) || - (setup_count && !setup)) { - SAFE_FREE(setup); - SAFE_FREE(params); - SAFE_FREE(data); - DEBUG(0,("reply_nttrans : Out of memory\n")); - END_PROFILE(SMBnttrans); - return ERROR_DOS(ERRDOS,ERRnomem); - } - - /* Copy the param and data bytes sent with this request into the params buffer */ - num_params_sofar = parameter_count; - num_data_sofar = data_count; - - if (parameter_count > total_parameter_count || data_count > total_data_count) + if ((dscnt > state->total_data) || (pscnt > state->total_param)) goto bad_param; - if(setup) { - DEBUG(10,("reply_nttrans: setup_count = %d\n", setup_count)); - if ((smb_nt_SetupStart + setup_count < smb_nt_SetupStart) || - (smb_nt_SetupStart + setup_count < setup_count)) { + if (state->total_data) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + if ((state->data = SMB_MALLOC(state->total_data)) == NULL) { + DEBUG(0,("reply_nttrans: data malloc fail for %u " + "bytes !\n", state->total_data)); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + if ((dsoff+dscnt < dsoff) || (dsoff+dscnt < dscnt)) goto bad_param; - } - if (smb_nt_SetupStart + setup_count > length) { + if ((smb_base(inbuf)+dsoff+dscnt > inbuf + size) || + (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf))) goto bad_param; - } - memcpy( setup, &inbuf[smb_nt_SetupStart], setup_count); - dump_data(10, setup, setup_count); + memcpy(state->data,smb_base(inbuf)+dsoff,dscnt); } - if(params) { - DEBUG(10,("reply_nttrans: parameter_count = %d\n", parameter_count)); - if ((parameter_offset + parameter_count < parameter_offset) || - (parameter_offset + parameter_count < parameter_count)) { + + if (state->total_param) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + if ((state->param = SMB_MALLOC(state->total_param)) == NULL) { + DEBUG(0,("reply_nttrans: param malloc fail for %u " + "bytes !\n", state->total_param)); + SAFE_FREE(state->data); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + if ((psoff+pscnt < psoff) || (psoff+pscnt < pscnt)) goto bad_param; - } - if ((smb_base(inbuf) + parameter_offset + parameter_count > inbuf + length)|| - (smb_base(inbuf) + parameter_offset + parameter_count < smb_base(inbuf))) { + if ((smb_base(inbuf)+psoff+pscnt > inbuf + size) || + (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf))) goto bad_param; - } - memcpy( params, smb_base(inbuf) + parameter_offset, parameter_count); - dump_data(10, params, parameter_count); + memcpy(state->param,smb_base(inbuf)+psoff,pscnt); } - if(data) { - DEBUG(10,("reply_nttrans: data_count = %d\n",data_count)); - if ((data_offset + data_count < data_offset) || (data_offset + data_count < data_count)) { + + state->received_data = dscnt; + state->received_param = pscnt; + + if(state->setup_count > 0) { + DEBUG(10,("reply_nttrans: state->setup_count = %d\n", + state->setup_count)); + state->setup = TALLOC(state, state->setup_count); + if (state->setup == NULL) { + DEBUG(0,("reply_nttrans : Out of memory\n")); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBnttrans); + return ERROR_DOS(ERRDOS,ERRnomem); + } + + if ((smb_nt_SetupStart + state->setup_count < smb_nt_SetupStart) || + (smb_nt_SetupStart + state->setup_count < state->setup_count)) { goto bad_param; } - if ((smb_base(inbuf) + data_offset + data_count > inbuf + length) || - (smb_base(inbuf) + data_offset + data_count < smb_base(inbuf))) { + if (smb_nt_SetupStart + state->setup_count > size) { goto bad_param; } - memcpy( data, smb_base(inbuf) + data_offset, data_count); - dump_data(10, data, data_count); + memcpy( state->setup, &inbuf[smb_nt_SetupStart], state->setup_count); + dump_data(10, (char *)state->setup, state->setup_count); } - srv_signing_trans_start(SVAL(inbuf,smb_mid)); + if ((state->received_data == state->total_data) && + (state->received_param == state->total_param)) { + outsize = handle_nttrans(conn, state, inbuf, outbuf, + size, bufsize); + SAFE_FREE(state->param); + SAFE_FREE(state->data); + TALLOC_FREE(state); + END_PROFILE(SMBnttrans); + return outsize; + } - if(num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) { - /* We need to send an interim response then receive the rest - of the parameter/data bytes */ - outsize = set_message(outbuf,0,0,True); - srv_signing_trans_stop(); - show_msg(outbuf); - if (!send_smb(smbd_server_fd(),outbuf)) { - exit_server("reply_nttrans: send_smb failed."); - } + DLIST_ADD(conn->pending_trans, state); - while( num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) { - BOOL ret; - uint32 parameter_displacement; - uint32 data_displacement; + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + show_msg(outbuf); + END_PROFILE(SMBnttrans); + return outsize; - ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); + bad_param: - /* We need to re-calcuate the new length after we've read the secondary packet. */ - length = smb_len(inbuf) + 4; + DEBUG(0,("reply_nttrans: invalid trans parameters\n")); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBnttrans); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); +} + +/**************************************************************************** + Reply to a SMBnttranss + ****************************************************************************/ - /* - * The sequence number for the trans reply is always - * based on the last secondary received. - */ +int reply_nttranss(connection_struct *conn, char *inbuf,char *outbuf, + int size,int bufsize) +{ + int outsize = 0; + unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; + struct trans_state *state; - srv_signing_trans_start(SVAL(inbuf,smb_mid)); + START_PROFILE(SMBnttranss); - if((ret && (CVAL(inbuf, smb_com) != SMBnttranss)) || !ret) { - outsize = set_message(outbuf,0,0,True); - if(ret) { - DEBUG(0,("reply_nttrans: Invalid secondary nttrans packet\n")); - } else { - DEBUG(0,("reply_nttrans: %s in getting secondary nttrans response.\n", - (smb_read_error == READ_ERROR) ? "error" : "timeout" )); - } - goto bad_param; - } - - /* Revise total_params and total_data in case they have changed downwards */ - if (IVAL(inbuf, smb_nts_TotalParameterCount) < total_parameter_count) { - total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount); - } - if (IVAL(inbuf, smb_nts_TotalDataCount) < total_data_count) { - total_data_count = IVAL(inbuf, smb_nts_TotalDataCount); - } + show_msg(inbuf); - parameter_count = IVAL(inbuf,smb_nts_ParameterCount); - parameter_offset = IVAL(inbuf, smb_nts_ParameterOffset); - parameter_displacement = IVAL(inbuf, smb_nts_ParameterDisplacement); - num_params_sofar += parameter_count; + for (state = conn->pending_trans; state != NULL; + state = state->next) { + if (state->mid == SVAL(inbuf,smb_mid)) { + break; + } + } - data_count = IVAL(inbuf, smb_nts_DataCount); - data_displacement = IVAL(inbuf, smb_nts_DataDisplacement); - data_offset = IVAL(inbuf, smb_nts_DataOffset); - num_data_sofar += data_count; + if ((state == NULL) || (state->cmd != SMBnttrans)) { + END_PROFILE(SMBnttranss); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } - if (num_params_sofar > total_parameter_count || num_data_sofar > total_data_count) { - DEBUG(0,("reply_nttrans2: data overflow in secondary nttrans packet")); - goto bad_param; - } + /* Revise state->total_param and state->total_data in case they have + changed downwards */ + if (IVAL(inbuf, smb_nts_TotalParameterCount) < state->total_param) { + state->total_param = IVAL(inbuf, smb_nts_TotalParameterCount); + } + if (IVAL(inbuf, smb_nts_TotalDataCount) < state->total_data) { + state->total_data = IVAL(inbuf, smb_nts_TotalDataCount); + } - if (parameter_count) { - if (parameter_displacement + parameter_count > total_parameter_count) { - goto bad_param; - } - if ((parameter_displacement + parameter_count < parameter_displacement) || - (parameter_displacement + parameter_count < parameter_count)) { - goto bad_param; - } - if (parameter_displacement > total_parameter_count) { - goto bad_param; - } - if ((smb_base(inbuf) + parameter_offset + parameter_count > inbuf + length) || - (smb_base(inbuf) + parameter_offset + parameter_count < smb_base(inbuf))) { - goto bad_param; - } - if (parameter_displacement + params < params) { - goto bad_param; - } + pcnt = IVAL(inbuf,smb_nts_ParameterCount); + poff = IVAL(inbuf, smb_nts_ParameterOffset); + pdisp = IVAL(inbuf, smb_nts_ParameterDisplacement); - memcpy( ¶ms[parameter_displacement], smb_base(inbuf) + parameter_offset, parameter_count); - } + dcnt = IVAL(inbuf, smb_nts_DataCount); + ddisp = IVAL(inbuf, smb_nts_DataDisplacement); + doff = IVAL(inbuf, smb_nts_DataOffset); - if (data_count) { - if (data_displacement + data_count > total_data_count) { - goto bad_param; - } - if ((data_displacement + data_count < data_displacement) || - (data_displacement + data_count < data_count)) { - goto bad_param; - } - if (data_displacement > total_data_count) { - goto bad_param; - } - if ((smb_base(inbuf) + data_offset + data_count > inbuf + length) || - (smb_base(inbuf) + data_offset + data_count < smb_base(inbuf))) { - goto bad_param; - } - if (data_displacement + data < data) { - goto bad_param; - } + state->received_param += pcnt; + state->received_data += dcnt; + + if ((state->received_data > state->total_data) || + (state->received_param > state->total_param)) + goto bad_param; - memcpy( &data[data_displacement], smb_base(inbuf)+ data_offset, data_count); - } - } - } + if (pcnt) { + if (pdisp+pcnt > state->total_param) + goto bad_param; + if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt)) + goto bad_param; + if (pdisp > state->total_param) + goto bad_param; + if ((smb_base(inbuf) + poff + pcnt > inbuf + size) || + (smb_base(inbuf) + poff + pcnt < smb_base(inbuf))) + goto bad_param; + if (state->param + pdisp < state->param) + goto bad_param; - if (Protocol >= PROTOCOL_NT1) { - SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_IS_LONG_NAME); + memcpy(state->param+pdisp,smb_base(inbuf)+poff, + pcnt); } - /* Now we must call the relevant NT_TRANS function */ - switch(function_code) { - case NT_TRANSACT_CREATE: - START_PROFILE_NESTED(NT_transact_create); - outsize = call_nt_transact_create(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_create); - break; - case NT_TRANSACT_IOCTL: - START_PROFILE_NESTED(NT_transact_ioctl); - outsize = call_nt_transact_ioctl(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_ioctl); - break; - case NT_TRANSACT_SET_SECURITY_DESC: - START_PROFILE_NESTED(NT_transact_set_security_desc); - outsize = call_nt_transact_set_security_desc(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_set_security_desc); - break; - case NT_TRANSACT_NOTIFY_CHANGE: - START_PROFILE_NESTED(NT_transact_notify_change); - outsize = call_nt_transact_notify_change(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_notify_change); - break; - case NT_TRANSACT_RENAME: - START_PROFILE_NESTED(NT_transact_rename); - outsize = call_nt_transact_rename(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_rename); - break; + if (dcnt) { + if (ddisp+dcnt > state->total_data) + goto bad_param; + if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt)) + goto bad_param; + if (ddisp > state->total_data) + goto bad_param; + if ((smb_base(inbuf) + doff + dcnt > inbuf + size) || + (smb_base(inbuf) + doff + dcnt < smb_base(inbuf))) + goto bad_param; + if (state->data + ddisp < state->data) + goto bad_param; - case NT_TRANSACT_QUERY_SECURITY_DESC: - START_PROFILE_NESTED(NT_transact_query_security_desc); - outsize = call_nt_transact_query_security_desc(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_query_security_desc); - break; -#ifdef HAVE_SYS_QUOTAS - case NT_TRANSACT_GET_USER_QUOTA: - START_PROFILE_NESTED(NT_transact_get_user_quota); - outsize = call_nt_transact_get_user_quota(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_get_user_quota); - break; - case NT_TRANSACT_SET_USER_QUOTA: - START_PROFILE_NESTED(NT_transact_set_user_quota); - outsize = call_nt_transact_set_user_quota(conn, inbuf, outbuf, - length, bufsize, - &setup, setup_count, - ¶ms, total_parameter_count, - &data, total_data_count, max_data_count); - END_PROFILE_NESTED(NT_transact_set_user_quota); - break; -#endif /* HAVE_SYS_QUOTAS */ - default: - /* Error in request */ - DEBUG(0,("reply_nttrans: Unknown request %d in nttrans call\n", function_code)); - SAFE_FREE(setup); - SAFE_FREE(params); - SAFE_FREE(data); - END_PROFILE(SMBnttrans); - srv_signing_trans_stop(); - return ERROR_DOS(ERRSRV,ERRerror); + memcpy(state->data+ddisp, smb_base(inbuf)+doff, + dcnt); } - /* As we do not know how many data packets will need to be - returned here the various call_nt_transact_xxxx calls - must send their own. Thus a call_nt_transact_xxxx routine only - returns a value other than -1 when it wants to send - an error packet. - */ + if ((state->received_param < state->total_param) || + (state->received_data < state->total_data)) { + END_PROFILE(SMBnttranss); + return -1; + } - srv_signing_trans_stop(); + /* construct_reply_common has done us the favor to pre-fill the + * command field with SMBnttranss which is wrong :-) + */ + SCVAL(outbuf,smb_com,SMBnttrans); - SAFE_FREE(setup); - SAFE_FREE(params); - SAFE_FREE(data); - END_PROFILE(SMBnttrans); - return outsize; /* If a correct response was needed the call_nt_transact_xxxx - calls have already sent it. If outsize != -1 then it is - returning an error packet. */ + outsize = handle_nttrans(conn, state, inbuf, outbuf, + size, bufsize); - bad_param: + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); - srv_signing_trans_stop(); - SAFE_FREE(params); - SAFE_FREE(data); - SAFE_FREE(setup); - END_PROFILE(SMBnttrans); + if (outsize == 0) { + END_PROFILE(SMBnttranss); + return(ERROR_DOS(ERRSRV,ERRnosupport)); + } + + END_PROFILE(SMBnttranss); + return(outsize); + + bad_param: + + DEBUG(0,("reply_nttranss: invalid trans parameters\n")); + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBnttranss); return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 40d26f7672..cdeccab5e8 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -505,22 +505,27 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) return receive_smb(smbd_server_fd(), buffer, 0); } -/**************************************************************************** -Get the next SMB packet, doing the local message processing automatically. -****************************************************************************/ +/* + * Only allow 5 outstanding trans requests. We're allocating memory, so + * prevent a DoS. + */ -BOOL receive_next_smb(char *inbuf, int bufsize, int timeout) +NTSTATUS allow_new_trans(struct trans_state *list, int mid) { - BOOL got_keepalive; - BOOL ret; + int count = 0; + for (; list != NULL; list = list->next) { - do { - ret = receive_message_or_smb(inbuf,bufsize,timeout); - - got_keepalive = (ret && (CVAL(inbuf,0) == SMBkeepalive)); - } while (ret && got_keepalive); + if (list->mid == mid) { + return NT_STATUS_INVALID_PARAMETER; + } + + count += 1; + } + if (count > 5) { + return NT_STATUS_INSUFFICIENT_RESOURCES; + } - return ret; + return NT_STATUS_OK; } /**************************************************************************** @@ -611,7 +616,7 @@ static const struct smb_message_struct { /* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER }, /* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER }, /* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC }, -/* 0x26 */ { "SMBtranss",NULL,AS_USER | CAN_IPC}, +/* 0x26 */ { "SMBtranss",reply_transs,AS_USER | CAN_IPC}, /* 0x27 */ { "SMBioctl",reply_ioctl,0}, /* 0x28 */ { "SMBioctls",NULL,AS_USER}, /* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE }, diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index ce41266a1c..040f7710fd 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -269,10 +269,13 @@ NTSTATUS check_path_syntax_wcard(pstring destname, const pstring srcname, BOOL * switch(next_mb_char_size(s)) { case 4: *d++ = *s++; + /*fall through*/ case 3: *d++ = *s++; + /*fall through*/ case 2: *d++ = *s++; + /*fall through*/ case 1: *d++ = *s++; break; @@ -374,10 +377,13 @@ NTSTATUS check_path_syntax_posix(pstring destname, const pstring srcname) switch(next_mb_char_size(s)) { case 4: *d++ = *s++; + /*fall through*/ case 3: *d++ = *s++; + /*fall through*/ case 2: *d++ = *s++; + /*fall through*/ case 1: *d++ = *s++; break; @@ -2319,7 +2325,7 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s /* ensure we don't overrun the packet size */ maxcount = MIN(65535,maxcount); - if (!is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK)) { + if (!is_locked(fsp,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK)) { SMB_STRUCT_STAT st; SMB_OFF_T size = 0; @@ -2390,8 +2396,13 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length * Note that the requested lock size is unaffected by max_recv. */ - status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), - (SMB_BIG_UINT)numtoread, (SMB_BIG_UINT)startpos, WRITE_LOCK, &my_lock_ctx); + status = do_lock_spin(fsp, + SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtoread, + (SMB_BIG_UINT)startpos, + WRITE_LOCK, + WINDOWS_LOCK, + &my_lock_ctx); if (NT_STATUS_V(status)) { #if 0 @@ -2407,8 +2418,15 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length * this smb into a queued request and push it * onto the blocking lock queue. */ - if(push_blocking_lock_request(inbuf, length, -1, 0, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)startpos, - (SMB_BIG_UINT)numtoread)) { + if(push_blocking_lock_request(inbuf, length, + fsp, + -1, + 0, + SVAL(inbuf,smb_pid), + WRITE_LOCK, + WINDOWS_LOCK, + (SMB_BIG_UINT)startpos, + (SMB_BIG_UINT)numtoread)) { END_PROFILE(SMBlockread); return -1; } @@ -2486,7 +2504,7 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n", data = smb_buf(outbuf) + 3; - if (is_locked(fsp,conn,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK)) { END_PROFILE(SMBread); return ERROR_DOS(ERRDOS,ERRlock); } @@ -2694,7 +2712,7 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt } - if (is_locked(fsp,conn,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK)) { END_PROFILE(SMBreadX); return ERROR_DOS(ERRDOS,ERRlock); } @@ -2757,7 +2775,7 @@ int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int size, SCVAL(inbuf,smb_com,SMBwritec); SCVAL(outbuf,smb_com,SMBwritec); - if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwritebraw); return(ERROR_DOS(ERRDOS,ERRlock)); } @@ -2878,7 +2896,7 @@ int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2); data = smb_buf(inbuf) + 3; - if (numtowrite && is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { + if (numtowrite && is_locked(fsp,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwriteunlock); return ERROR_DOS(ERRDOS,ERRlock); } @@ -2900,8 +2918,12 @@ int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, } if (numtowrite) { - status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtowrite, - (SMB_BIG_UINT)startpos); + status = do_unlock(fsp, + SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtowrite, + (SMB_BIG_UINT)startpos, + WINDOWS_LOCK); + if (NT_STATUS_V(status)) { END_PROFILE(SMBwriteunlock); return ERROR_NT(status); @@ -2951,7 +2973,7 @@ int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int d startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2); data = smb_buf(inbuf) + 3; - if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwrite); return ERROR_DOS(ERRDOS,ERRlock); } @@ -3066,7 +3088,7 @@ int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int leng #endif /* LARGE_SMB_OFF_T */ } - if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwriteX); return ERROR_DOS(ERRDOS,ERRlock); } @@ -3340,7 +3362,7 @@ int reply_writeclose(connection_struct *conn, mtime = srv_make_unix_date3(inbuf+smb_vwv4); data = smb_buf(inbuf) + 1; - if (numtowrite && is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { + if (numtowrite && is_locked(fsp,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { END_PROFILE(SMBwriteclose); return ERROR_DOS(ERRDOS,ERRlock); } @@ -3410,7 +3432,13 @@ int reply_lock(connection_struct *conn, DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n", fsp->fh->fd, fsp->fnum, (double)offset, (double)count)); - status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, &my_lock_ctx); + status = do_lock_spin(fsp, + SVAL(inbuf,smb_pid), + count, + offset, + WRITE_LOCK, + WINDOWS_LOCK, + &my_lock_ctx); if (NT_STATUS_V(status)) { #if 0 /* Tests using Samba4 against W2K show this call never creates a blocking lock. */ @@ -3420,7 +3448,14 @@ int reply_lock(connection_struct *conn, * this smb into a queued request and push it * onto the blocking lock queue. */ - if(push_blocking_lock_request(inbuf, length, -1, 0, SVAL(inbuf,smb_pid), offset, count)) { + if(push_blocking_lock_request(inbuf, length, + fsp, + -1, + 0, + SVAL(inbuf,smb_pid), + WRITE_LOCK, + WINDOWS_LOCK, + offset, count)) { END_PROFILE(SMBlock); return -1; } @@ -3452,7 +3487,12 @@ int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int size, count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1); offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3); - status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), count, offset); + status = do_unlock(fsp, + SVAL(inbuf,smb_pid), + count, + offset, + WINDOWS_LOCK); + if (NT_STATUS_V(status)) { END_PROFILE(SMBunlock); return ERROR_NT(status); @@ -5279,7 +5319,12 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, "pid %u, file %s\n", (double)offset, (double)count, (unsigned int)lock_pid, fsp->fsp_name )); - status = do_unlock(fsp,conn,lock_pid,count,offset); + status = do_unlock(fsp, + lock_pid, + count, + offset, + WINDOWS_LOCK); + if (NT_STATUS_V(status)) { END_PROFILE(SMBlockingX); return ERROR_NT(status); @@ -5297,6 +5342,7 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, of smb_lkrng structs */ for(i = 0; i < (int)num_locks; i++) { + enum brl_type lock_type = ((locktype & 1) ? READ_LOCK:WRITE_LOCK); lock_pid = get_lock_pid( data, i, large_file_format); count = get_lock_count( data, i, large_file_format); offset = get_lock_offset( data, i, large_file_format, &err); @@ -5314,9 +5360,14 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, (double)count, (unsigned int)lock_pid, fsp->fsp_name, (int)lock_timeout )); - status = do_lock_spin(fsp,conn,lock_pid, count,offset, - ((locktype & 1) ? READ_LOCK:WRITE_LOCK), - &my_lock_ctx); + status = do_lock_spin(fsp, + lock_pid, + count, + offset, + lock_type, + WINDOWS_LOCK, + &my_lock_ctx); + if (NT_STATUS_V(status)) { /* * Interesting fact found by IFSTEST /t @@ -5334,8 +5385,13 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, * onto the blocking lock queue. */ if(push_blocking_lock_request(inbuf, length, - lock_timeout, i, - lock_pid, offset, + fsp, + lock_timeout, + i, + lock_pid, + lock_type, + WINDOWS_LOCK, + offset, count)) { END_PROFILE(SMBlockingX); return -1; @@ -5368,7 +5424,11 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, return ERROR_DOS(ERRDOS,ERRnoaccess); } - do_unlock(fsp,conn,lock_pid,count,offset); + do_unlock(fsp, + lock_pid, + count, + offset, + WINDOWS_LOCK); } END_PROFILE(SMBlockingX); return ERROR_NT(status); @@ -5430,7 +5490,7 @@ int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length, tcount = maxcount; total_read = 0; - if (is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK)) { END_PROFILE(SMBreadBmpx); return ERROR_DOS(ERRDOS,ERRlock); } @@ -5562,7 +5622,7 @@ int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int size, not an SMBwritebmpx - set this up now so we don't forget */ SCVAL(outbuf,smb_com,SMBwritec); - if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos,WRITE_LOCK)) { + if (is_locked(fsp,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos,WRITE_LOCK)) { END_PROFILE(SMBwriteBmpx); return(ERROR_DOS(ERRDOS,ERRlock)); } diff --git a/source3/smbd/server.c b/source3/smbd/server.c index ba31827eb3..0880778f55 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -898,9 +898,8 @@ void build_options(BOOL screen); * If we're interactive we want to set our own process group for * signal management. */ - if (interactive && !no_process_group) { + if (interactive && !no_process_group) setpgid( (pid_t)0, (pid_t)0); - } #endif if (!directory_exist(lp_lockdir(), NULL)) diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 4f5039e86c..19463403ac 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -563,7 +563,7 @@ static struct ea_list *ea_list_union(struct ea_list *name_list, struct ea_list * HACK ! Always assumes smb_setup field is zero. ****************************************************************************/ -static int send_trans2_replies(char *outbuf, +int send_trans2_replies(char *outbuf, int bufsize, char *params, int paramsize, @@ -1644,11 +1644,12 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n", case SMB_FIND_ID_BOTH_DIRECTORY_INFO: break; case SMB_FIND_FILE_UNIX: - if (!lp_unix_extensions()) - return(ERROR_DOS(ERRDOS,ERRunknownlevel)); + if (!lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } break; default: - return(ERROR_DOS(ERRDOS,ERRunknownlevel)); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } srvstr_get_path_wcard(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus, &mask_contains_wcard); @@ -1926,11 +1927,12 @@ resume_key = %d resume name = %s continue=%d level = %d\n", case SMB_FIND_ID_BOTH_DIRECTORY_INFO: break; case SMB_FIND_FILE_UNIX: - if (!lp_unix_extensions()) - return(ERROR_DOS(ERRDOS,ERRunknownlevel)); + if (!lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } break; default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } if (info_level == SMB_FIND_EA_LIST) { @@ -2398,13 +2400,21 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned */ case SMB_QUERY_CIFS_UNIX_INFO: - if (!lp_unix_extensions()) - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (!lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } data_len = 12; SSVAL(pdata,0,CIFS_UNIX_MAJOR_VERSION); SSVAL(pdata,2,CIFS_UNIX_MINOR_VERSION); - SBIG_UINT(pdata,4,((SMB_BIG_UINT)(CIFS_UNIX_POSIX_ACLS_CAP| - CIFS_UNIX_POSIX_PATHNAMES_CAP))); /* We have POSIX ACLs and pathname capability. */ + /* We have POSIX ACLs, pathname and locking capability. */ + SBIG_UINT(pdata,4,((SMB_BIG_UINT)( + CIFS_UNIX_POSIX_ACLS_CAP| + CIFS_UNIX_POSIX_PATHNAMES_CAP| +#if defined(DEVELOPER) /* Not quite finished yet... */ + CIFS_UNIX_FCNTL_LOCKS_CAP))); +#else + 0))); +#endif break; case SMB_QUERY_POSIX_FS_INFO: @@ -2412,9 +2422,10 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int rc; vfs_statvfs_struct svfs; - if (!lp_unix_extensions()) - return ERROR_DOS(ERRDOS,ERRunknownlevel); - + if (!lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } + rc = SMB_VFS_STATVFS(conn, ".", &svfs); if (!rc) { @@ -2430,7 +2441,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_POSIX_FS_INFO succsessful\n")); #ifdef EOPNOTSUPP } else if (rc == EOPNOTSUPP) { - return ERROR_DOS(ERRDOS, ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); #endif /* EOPNOTSUPP */ } else { DEBUG(0,("vfs_statvfs() failed for service [%s]\n",lp_servicename(SNUM(conn)))); @@ -2451,7 +2462,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned } /* drop through */ default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } @@ -2495,7 +2506,7 @@ static int call_trans2setfsinfo(connection_struct *conn, char *inbuf, char *outb uint32 client_unix_cap_high; if (!lp_unix_extensions()) { - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } /* There should be 12 bytes of capabilities set. */ @@ -2515,8 +2526,15 @@ cap_low = 0x%x, cap_high = 0x%x\n", (unsigned int)client_unix_cap_high )); /* Here is where we must switch to posix pathname processing... */ - lp_set_posix_pathnames(); - mangle_change_to_posix(); + if (client_unix_cap_low & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + lp_set_posix_pathnames(); + mangle_change_to_posix(); + } +#if defined(DEVELOPER) + if (client_unix_cap_low & CIFS_UNIX_FCNTL_LOCKS_CAP) { + lp_set_posix_cifsx_locktype(POSIX_LOCK); + } +#endif break; } case SMB_FS_QUOTA_INFORMATION: @@ -2593,7 +2611,7 @@ cap_low = 0x%x, cap_high = 0x%x\n", default: DEBUG(3,("call_trans2setfsinfo: unknown level (0x%X) not implemented yet.\n", info_level)); - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); break; } @@ -2768,13 +2786,14 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * char *fullpathname; char *base_name; char *p; + char *lock_data = NULL; SMB_OFF_T pos = 0; BOOL bad_path = False; BOOL delete_pending = False; int len; time_t c_time; files_struct *fsp = NULL; - TALLOC_CTX *ea_ctx = NULL; + TALLOC_CTX *data_ctx = NULL; struct ea_list *ea_list = NULL; uint32 access_mask = 0x12019F; /* Default - GENERIC_EXECUTE mapping from Windows */ @@ -2893,8 +2912,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * nlink -= 1; } - if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } DEBUG(3,("call_trans2qfilepathinfo %s (fnum = %d) level=%d call=%d total_data=%d\n", fname,fsp ? fsp->fnum : -1, info_level,tran_call,total_data)); @@ -2913,40 +2933,70 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * if (!(mode & aDIR)) file_size = get_file_size(sbuf); - /* Pull any EA list from the data portion. */ - if (info_level == SMB_INFO_QUERY_EAS_FROM_LIST) { - uint32 ea_size; + /* Pull out any data sent here before we realloc. */ + switch (info_level) { + case SMB_INFO_QUERY_EAS_FROM_LIST: + { + /* Pull any EA list from the data portion. */ + uint32 ea_size; - if (total_data < 4) { - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); - } - ea_size = IVAL(pdata,0); + if (total_data < 4) { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + ea_size = IVAL(pdata,0); - if (total_data > 0 && ea_size != total_data) { - DEBUG(4,("call_trans2qfilepathinfo: Rejecting EA request with incorrect \ + if (total_data > 0 && ea_size != total_data) { + DEBUG(4,("call_trans2qfilepathinfo: Rejecting EA request with incorrect \ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) )); - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); - } + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } - if (!lp_ea_support(SNUM(conn))) { - return ERROR_DOS(ERRDOS,ERReasnotsupported); - } + if (!lp_ea_support(SNUM(conn))) { + return ERROR_DOS(ERRDOS,ERReasnotsupported); + } - if ((ea_ctx = talloc_init("ea_list")) == NULL) { - return ERROR_NT(NT_STATUS_NO_MEMORY); + if ((data_ctx = talloc_init("ea_list")) == NULL) { + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + /* Pull out the list of names. */ + ea_list = read_ea_name_list(data_ctx, pdata + 4, ea_size - 4); + if (!ea_list) { + talloc_destroy(data_ctx); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + break; } +#if defined(DEVELOPER) + case SMB_QUERY_POSIX_LOCK: + { + if (fsp == NULL || fsp->fh->fd == -1) { + return ERROR_NT(NT_STATUS_INVALID_HANDLE); + } - /* Pull out the list of names. */ - ea_list = read_ea_name_list(ea_ctx, pdata + 4, ea_size - 4); - if (!ea_list) { - talloc_destroy(ea_ctx); - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + if (total_data != POSIX_LOCK_DATA_SIZE) { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + if ((data_ctx = talloc_init("lock_request")) == NULL) { + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + /* Copy the lock range data. */ + lock_data = talloc_memdup(data_ctx, pdata, total_data); + if (!lock_data) { + talloc_destroy(data_ctx); + return ERROR_NT(NT_STATUS_NO_MEMORY); + } } +#endif + default: + break; } *pparams = SMB_REALLOC(*pparams,2); if (*pparams == NULL) { - talloc_destroy(ea_ctx); + talloc_destroy(data_ctx); return ERROR_NT(NT_STATUS_NO_MEMORY); } params = *pparams; @@ -2954,7 +3004,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN; *ppdata = SMB_REALLOC(*ppdata, data_size); if (*ppdata == NULL ) { - talloc_destroy(ea_ctx); + talloc_destroy(data_ctx); return ERROR_NT(NT_STATUS_NO_MEMORY); } pdata = *ppdata; @@ -3040,18 +3090,18 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd DEBUG(10,("call_trans2qfilepathinfo: SMB_INFO_QUERY_EAS_FROM_LIST\n")); - ea_file_list = get_ea_list_from_file(ea_ctx, conn, fsp, fname, &total_ea_len); + ea_file_list = get_ea_list_from_file(data_ctx, conn, fsp, fname, &total_ea_len); ea_list = ea_list_union(ea_list, ea_file_list, &total_ea_len); if (!ea_list || (total_ea_len > data_size)) { - talloc_destroy(ea_ctx); + talloc_destroy(data_ctx); data_size = 4; SIVAL(pdata,0,4); /* EA List Length must be set to 4 if no EA's. */ break; } - data_size = fill_ea_buffer(ea_ctx, pdata, data_size, conn, ea_list); - talloc_destroy(ea_ctx); + data_size = fill_ea_buffer(data_ctx, pdata, data_size, conn, ea_list); + talloc_destroy(data_ctx); break; } @@ -3062,21 +3112,21 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd DEBUG(10,("call_trans2qfilepathinfo: SMB_INFO_QUERY_ALL_EAS\n")); - ea_ctx = talloc_init("ea_ctx"); - if (!ea_ctx) { + data_ctx = talloc_init("ea_ctx"); + if (!data_ctx) { return ERROR_NT(NT_STATUS_NO_MEMORY); } - ea_list = get_ea_list_from_file(ea_ctx, conn, fsp, fname, &total_ea_len); + ea_list = get_ea_list_from_file(data_ctx, conn, fsp, fname, &total_ea_len); if (!ea_list || (total_ea_len > data_size)) { - talloc_destroy(ea_ctx); + talloc_destroy(data_ctx); data_size = 4; SIVAL(pdata,0,4); /* EA List Length must be set to 4 if no EA's. */ break; } - data_size = fill_ea_buffer(ea_ctx, pdata, data_size, conn, ea_list); - talloc_destroy(ea_ctx); + data_size = fill_ea_buffer(data_ctx, pdata, data_size, conn, ea_list); + talloc_destroy(data_ctx); break; } @@ -3469,8 +3519,84 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd } #endif + +#if defined(DEVELOPER) + case SMB_QUERY_POSIX_LOCK: + { + NTSTATUS status = NT_STATUS_INVALID_LEVEL; + SMB_BIG_UINT count; + SMB_BIG_UINT offset; + uint16 lock_pid; + enum brl_type lock_type; + + if (total_data != POSIX_LOCK_DATA_SIZE) { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + switch (SVAL(pdata, POSIX_LOCK_TYPE_OFFSET)) { + case POSIX_LOCK_TYPE_READ: + lock_type = READ_LOCK; + break; + case POSIX_LOCK_TYPE_WRITE: + lock_type = WRITE_LOCK; + break; + case POSIX_LOCK_TYPE_UNLOCK: + default: + /* There's no point in asking for an unlock... */ + talloc_destroy(data_ctx); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + lock_pid = (uint16)IVAL(pdata, POSIX_LOCK_PID_OFFSET); +#if defined(HAVE_LONGLONG) + offset = (((SMB_BIG_UINT) IVAL(pdata,(POSIX_LOCK_START_OFFSET+4))) << 32) | + ((SMB_BIG_UINT) IVAL(pdata,POSIX_LOCK_START_OFFSET)); + count = (((SMB_BIG_UINT) IVAL(pdata,(POSIX_LOCK_LEN_OFFSET+4))) << 32) | + ((SMB_BIG_UINT) IVAL(pdata,POSIX_LOCK_LEN_OFFSET)); +#else /* HAVE_LONGLONG */ + offset = (SMB_BIG_UINT)IVAL(pdata,POSIX_LOCK_START_OFFSET); + count = (SMB_BIG_UINT)IVAL(pdata,POSIX_LOCK_LEN_OFFSET); +#endif /* HAVE_LONGLONG */ + + status = query_lock(fsp, + &lock_pid, + &count, + &offset, + &lock_type, + POSIX_LOCK); + + if (ERROR_WAS_LOCK_DENIED(status)) { + /* Here we need to report who has it locked... */ + data_size = POSIX_LOCK_DATA_SIZE; + + SSVAL(pdata, POSIX_LOCK_TYPE_OFFSET, lock_type); + SSVAL(pdata, POSIX_LOCK_FLAGS_OFFSET, 0); + SIVAL(pdata, POSIX_LOCK_PID_OFFSET, lock_pid); +#if defined(HAVE_LONGLONG) + SIVAL(pdata, POSIX_LOCK_START_OFFSET, (uint32)(offset & 0xFFFFFFFF)); + SIVAL(pdata, POSIX_LOCK_START_OFFSET + 4, (uint32)((offset >> 32) & 0xFFFFFFFF)); + SIVAL(pdata, POSIX_LOCK_LEN_OFFSET, (uint32)(count & 0xFFFFFFFF)); + SIVAL(pdata, POSIX_LOCK_LEN_OFFSET + 4, (uint32)((count >> 32) & 0xFFFFFFFF)); +#else /* HAVE_LONGLONG */ + SIVAL(pdata, POSIX_LOCK_START_OFFSET, offset); + SIVAL(pdata, POSIX_LOCK_LEN_OFFSET, count); +#endif /* HAVE_LONGLONG */ + + } else if (NT_STATUS_IS_OK(status)) { + /* For success we just return a copy of what we sent + with the lock type set to POSIX_LOCK_TYPE_UNLOCK. */ + data_size = POSIX_LOCK_DATA_SIZE; + memcpy(pdata, lock_data, POSIX_LOCK_DATA_SIZE); + SSVAL(pdata, POSIX_LOCK_TYPE_OFFSET, POSIX_LOCK_TYPE_UNLOCK); + } else { + return ERROR_NT(status); + } + break; + } +#endif + default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } send_trans2_replies(outbuf, bufsize, params, param_size, *ppdata, data_size); @@ -3674,8 +3800,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char if (!CAN_WRITE(conn)) return ERROR_DOS(ERRSRV,ERRaccess); - if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } if (VALID_STAT(sbuf)) unixmode = sbuf.st_mode; @@ -3843,8 +3970,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char #ifdef LARGE_SMB_OFF_T allocation_size |= (((SMB_BIG_UINT)IVAL(pdata,4)) << 32); #else /* LARGE_SMB_OFF_T */ - if (IVAL(pdata,4) != 0) /* more than 32 bits? */ - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (IVAL(pdata,4) != 0) { + /* more than 32 bits? */ + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } #endif /* LARGE_SMB_OFF_T */ DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n", fname, (double)allocation_size )); @@ -3910,8 +4039,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char #ifdef LARGE_SMB_OFF_T size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); #else /* LARGE_SMB_OFF_T */ - if (IVAL(pdata,4) != 0) /* more than 32 bits? */ - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (IVAL(pdata,4) != 0) { + /* more than 32 bits? */ + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } #endif /* LARGE_SMB_OFF_T */ DEBUG(10,("call_trans2setfilepathinfo: Set end of file info for file %s to %.0f\n", fname, (double)size )); break; @@ -3964,8 +4095,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char #ifdef LARGE_SMB_OFF_T position_information |= (((SMB_BIG_UINT)IVAL(pdata,4)) << 32); #else /* LARGE_SMB_OFF_T */ - if (IVAL(pdata,4) != 0) /* more than 32 bits? */ - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (IVAL(pdata,4) != 0) { + /* more than 32 bits? */ + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } #endif /* LARGE_SMB_OFF_T */ DEBUG(10,("call_trans2setfilepathinfo: Set file position information for file %s to %.0f\n", fname, (double)position_information )); @@ -4021,8 +4154,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char #ifdef LARGE_SMB_OFF_T size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); #else /* LARGE_SMB_OFF_T */ - if (IVAL(pdata,4) != 0) /* more than 32 bits? */ - return ERROR_DOS(ERRDOS,ERRunknownlevel); + if (IVAL(pdata,4) != 0) { + /* more than 32 bits? */ + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } #endif /* LARGE_SMB_OFF_T */ } pdata+=24; /* ctime & st_blocks are not changed */ @@ -4329,8 +4464,109 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", } #endif +#if defined(DEVELOPER) + case SMB_SET_POSIX_LOCK: + { + SMB_BIG_UINT count; + SMB_BIG_UINT offset; + uint16 lock_pid; + BOOL lock_blocking; + enum brl_type lock_type; + BOOL my_lock_ctx; + + if (fsp == NULL || fsp->fh->fd == -1) { + return ERROR_NT(NT_STATUS_INVALID_HANDLE); + } + + if (total_data != POSIX_LOCK_DATA_SIZE) { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + switch (SVAL(pdata, POSIX_LOCK_TYPE_OFFSET)) { + case POSIX_LOCK_TYPE_READ: + lock_type = READ_LOCK; + break; + case POSIX_LOCK_TYPE_WRITE: + /* Return the right POSIX-mappable error code for files opened read-only. */ + if (!fsp->can_write) { + return ERROR_NT(NT_STATUS_INVALID_HANDLE); + } + lock_type = WRITE_LOCK; + break; + case POSIX_LOCK_TYPE_UNLOCK: + lock_type = UNLOCK_LOCK; + break; + default: + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + if (SVAL(pdata,POSIX_LOCK_FLAGS_OFFSET) == POSIX_LOCK_FLAG_NOWAIT) { + lock_blocking = False; + } else if (SVAL(pdata,POSIX_LOCK_FLAGS_OFFSET) == POSIX_LOCK_FLAG_WAIT) { + lock_blocking = True; + } else { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + lock_pid = (uint16)IVAL(pdata, POSIX_LOCK_PID_OFFSET); +#if defined(HAVE_LONGLONG) + offset = (((SMB_BIG_UINT) IVAL(pdata,(POSIX_LOCK_START_OFFSET+4))) << 32) | + ((SMB_BIG_UINT) IVAL(pdata,POSIX_LOCK_START_OFFSET)); + count = (((SMB_BIG_UINT) IVAL(pdata,(POSIX_LOCK_LEN_OFFSET+4))) << 32) | + ((SMB_BIG_UINT) IVAL(pdata,POSIX_LOCK_LEN_OFFSET)); +#else /* HAVE_LONGLONG */ + offset = (SMB_BIG_UINT)IVAL(pdata,POSIX_LOCK_START_OFFSET); + count = (SMB_BIG_UINT)IVAL(pdata,POSIX_LOCK_LEN_OFFSET); +#endif /* HAVE_LONGLONG */ + + if (lock_type == UNLOCK_LOCK) { + status = do_unlock(fsp, + lock_pid, + count, + offset, + POSIX_LOCK); + } else { + status = do_lock(fsp, + lock_pid, + count, + offset, + lock_type, + POSIX_LOCK, + &my_lock_ctx); + + /* TODO: Deal with rescheduling blocking lock fail here... */ + if (lp_blocking_locks(SNUM(conn)) && ERROR_WAS_LOCK_DENIED(status)) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, + fsp, + -1, /* infinite timeout. */ + 0, + lock_pid, + lock_type, + POSIX_LOCK, + offset, + count)) { + return -1; + } + } + } + + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + + SSVAL(params,0,0); + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); + return(-1); + } +#endif + default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } /* get some defaults (no modifications) if any info is zero or -1. */ @@ -4582,7 +4818,7 @@ static int call_trans2findnotifyfirst(connection_struct *conn, char *inbuf, char case 2: break; default: - return ERROR_DOS(ERRDOS,ERRunknownlevel); + return ERROR_NT(NT_STATUS_INVALID_LEVEL); } /* Realloc the parameter and data sizes */ @@ -4759,331 +4995,418 @@ int reply_findnclose(connection_struct *conn, return(outsize); } -/**************************************************************************** - Reply to a SMBtranss2 - just ignore it! -****************************************************************************/ - -int reply_transs2(connection_struct *conn, - char *inbuf,char *outbuf,int length,int bufsize) -{ - START_PROFILE(SMBtranss2); - DEBUG(4,("Ignoring transs2 of length %d\n",length)); - END_PROFILE(SMBtranss2); - return(-1); -} - -/**************************************************************************** - Reply to a SMBtrans2. -****************************************************************************/ - -int reply_trans2(connection_struct *conn, - char *inbuf,char *outbuf,int length,int bufsize) +int handle_trans2(connection_struct *conn, + struct trans_state *state, + char *inbuf, char *outbuf, int size, int bufsize) { - int outsize = 0; - unsigned int total_params = SVAL(inbuf, smb_tpscnt); - unsigned int total_data =SVAL(inbuf, smb_tdscnt); - unsigned int max_data_bytes = SVAL(inbuf, smb_mdrcnt); -#if 0 - unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt); - unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt); - BOOL close_tid = BITSETW(inbuf+smb_flags,0); - BOOL no_final_response = BITSETW(inbuf+smb_flags,1); - int32 timeout = IVALS(inbuf,smb_timeout); -#endif - unsigned int suwcnt = SVAL(inbuf, smb_suwcnt); - unsigned int tran_call = SVAL(inbuf, smb_setup0); - char *params = NULL, *data = NULL; - unsigned int num_params, num_params_sofar, num_data, num_data_sofar; - START_PROFILE(SMBtrans2); - - if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN) - && (tran_call != TRANSACT2_GET_DFS_REFERRAL)) { - END_PROFILE(SMBtrans2); - return ERROR_DOS(ERRSRV,ERRaccess); - } - - outsize = set_message(outbuf,0,0,True); - - /* All trans2 messages we handle have smb_sucnt == 1 - ensure this - is so as a sanity check */ - if (suwcnt != 1) { - /* - * Need to have rc=0 for ioctl to get job id for OS/2. - * Network printing will fail if function is not successful. - * Similar function in reply.c will be used if protocol - * is LANMAN1.0 instead of LM1.2X002. - * Until DosPrintSetJobInfo with PRJINFO3 is supported, - * outbuf doesn't have to be set(only job id is used). - */ - if ( (suwcnt == 4) && (tran_call == TRANSACT2_IOCTL) && - (SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) && - (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) { - DEBUG(2,("Got Trans2 DevIOctl jobid\n")); - } else { - DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",suwcnt)); - DEBUG(2,("Transaction is %d\n",tran_call)); - END_PROFILE(SMBtrans2); - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); - } - } - - /* Allocate the space for the maximum needed parameters and data */ - if (total_params > 0) - params = (char *)SMB_MALLOC(total_params); - if (total_data > 0) - data = (char *)SMB_MALLOC(total_data); - - if ((total_params && !params) || (total_data && !data)) { - DEBUG(2,("Out of memory in reply_trans2\n")); - SAFE_FREE(params); - SAFE_FREE(data); - END_PROFILE(SMBtrans2); - return ERROR_NT(NT_STATUS_NO_MEMORY); - } - - /* Copy the param and data bytes sent with this request into - the params buffer */ - num_params = num_params_sofar = SVAL(inbuf,smb_pscnt); - num_data = num_data_sofar = SVAL(inbuf, smb_dscnt); - - if (num_params > total_params || num_data > total_data) - exit_server("invalid params in reply_trans2"); - - if(params) { - unsigned int psoff = SVAL(inbuf, smb_psoff); - if ((psoff + num_params < psoff) || (psoff + num_params < num_params)) - goto bad_param; - if ((smb_base(inbuf) + psoff + num_params > inbuf + length) || - (smb_base(inbuf) + psoff + num_params < smb_base(inbuf))) - goto bad_param; - memcpy( params, smb_base(inbuf) + psoff, num_params); - } - if(data) { - unsigned int dsoff = SVAL(inbuf, smb_dsoff); - if ((dsoff + num_data < dsoff) || (dsoff + num_data < num_data)) - goto bad_param; - if ((smb_base(inbuf) + dsoff + num_data > inbuf + length) || - (smb_base(inbuf) + dsoff + num_data < smb_base(inbuf))) - goto bad_param; - memcpy( data, smb_base(inbuf) + dsoff, num_data); - } - - srv_signing_trans_start(SVAL(inbuf,smb_mid)); - - if(num_data_sofar < total_data || num_params_sofar < total_params) { - /* We need to send an interim response then receive the rest - of the parameter/data bytes */ - outsize = set_message(outbuf,0,0,True); - srv_signing_trans_stop(); - show_msg(outbuf); - if (!send_smb(smbd_server_fd(),outbuf)) - exit_server("reply_trans2: send_smb failed."); - - while (num_data_sofar < total_data || - num_params_sofar < total_params) { - BOOL ret; - unsigned int param_disp; - unsigned int param_off; - unsigned int data_disp; - unsigned int data_off; - - ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); - - /* We need to re-calcuate the new length after we've read the secondary packet. */ - length = smb_len(inbuf) + 4; - - /* - * The sequence number for the trans reply is always - * based on the last secondary received. - */ + int outsize; - srv_signing_trans_start(SVAL(inbuf,smb_mid)); - - if ((ret && - (CVAL(inbuf, smb_com) != SMBtranss2)) || !ret) { - outsize = set_message(outbuf,0,0,True); - if(ret) - DEBUG(0,("reply_trans2: Invalid secondary trans2 packet\n")); - else - DEBUG(0,("reply_trans2: %s in getting secondary trans2 response.\n", - (smb_read_error == READ_ERROR) ? "error" : "timeout" )); - goto bad_param; - } - - /* Revise total_params and total_data in case - they have changed downwards */ - if (SVAL(inbuf, smb_tpscnt) < total_params) - total_params = SVAL(inbuf, smb_tpscnt); - if (SVAL(inbuf, smb_tdscnt) < total_data) - total_data = SVAL(inbuf, smb_tdscnt); - - num_params = SVAL(inbuf,smb_spscnt); - param_off = SVAL(inbuf, smb_spsoff); - param_disp = SVAL(inbuf, smb_spsdisp); - num_params_sofar += num_params; - - num_data = SVAL(inbuf, smb_sdscnt); - data_off = SVAL(inbuf, smb_sdsoff); - data_disp = SVAL(inbuf, smb_sdsdisp); - num_data_sofar += num_data; - - if (num_params_sofar > total_params || num_data_sofar > total_data) - goto bad_param; - - if (num_params) { - if (param_disp + num_params > total_params) - goto bad_param; - if ((param_disp + num_params < param_disp) || - (param_disp + num_params < num_params)) - goto bad_param; - if (param_disp > total_params) - goto bad_param; - if ((smb_base(inbuf) + param_off + num_params > inbuf + length) || - (smb_base(inbuf) + param_off + num_params < smb_base(inbuf))) - goto bad_param; - if (params + param_disp < params) - goto bad_param; - - memcpy( ¶ms[param_disp], smb_base(inbuf) + param_off, num_params); - } - if (num_data) { - if (data_disp + num_data > total_data) - goto bad_param; - if ((data_disp + num_data < data_disp) || - (data_disp + num_data < num_data)) - goto bad_param; - if (data_disp > total_data) - goto bad_param; - if ((smb_base(inbuf) + data_off + num_data > inbuf + length) || - (smb_base(inbuf) + data_off + num_data < smb_base(inbuf))) - goto bad_param; - if (data + data_disp < data) - goto bad_param; - - memcpy( &data[data_disp], smb_base(inbuf) + data_off, num_data); - } - } - } - if (Protocol >= PROTOCOL_NT1) { SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | 0x40); /* IS_LONG_NAME */ } /* Now we must call the relevant TRANS2 function */ - switch(tran_call) { + switch(state->call) { case TRANSACT2_OPEN: START_PROFILE_NESTED(Trans2_open); - outsize = call_trans2open(conn, inbuf, outbuf, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2open( + conn, inbuf, outbuf, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_open); break; case TRANSACT2_FINDFIRST: START_PROFILE_NESTED(Trans2_findfirst); - outsize = call_trans2findfirst(conn, inbuf, outbuf, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2findfirst( + conn, inbuf, outbuf, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_findfirst); break; case TRANSACT2_FINDNEXT: START_PROFILE_NESTED(Trans2_findnext); - outsize = call_trans2findnext(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2findnext( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_findnext); break; case TRANSACT2_QFSINFO: START_PROFILE_NESTED(Trans2_qfsinfo); - outsize = call_trans2qfsinfo(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2qfsinfo( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_qfsinfo); break; case TRANSACT2_SETFSINFO: START_PROFILE_NESTED(Trans2_setfsinfo); - outsize = call_trans2setfsinfo(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2setfsinfo( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_setfsinfo); break; case TRANSACT2_QPATHINFO: case TRANSACT2_QFILEINFO: START_PROFILE_NESTED(Trans2_qpathinfo); - outsize = call_trans2qfilepathinfo(conn, inbuf, outbuf, length, bufsize, tran_call, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2qfilepathinfo( + conn, inbuf, outbuf, size, bufsize, state->call, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_qpathinfo); break; case TRANSACT2_SETPATHINFO: case TRANSACT2_SETFILEINFO: START_PROFILE_NESTED(Trans2_setpathinfo); - outsize = call_trans2setfilepathinfo(conn, inbuf, outbuf, length, bufsize, tran_call, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2setfilepathinfo( + conn, inbuf, outbuf, size, bufsize, state->call, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_setpathinfo); break; case TRANSACT2_FINDNOTIFYFIRST: START_PROFILE_NESTED(Trans2_findnotifyfirst); - outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2findnotifyfirst( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_findnotifyfirst); break; case TRANSACT2_FINDNOTIFYNEXT: START_PROFILE_NESTED(Trans2_findnotifynext); - outsize = call_trans2findnotifynext(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2findnotifynext( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_findnotifynext); break; case TRANSACT2_MKDIR: START_PROFILE_NESTED(Trans2_mkdir); - outsize = call_trans2mkdir(conn, inbuf, outbuf, length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2mkdir( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_mkdir); break; case TRANSACT2_GET_DFS_REFERRAL: START_PROFILE_NESTED(Trans2_get_dfs_referral); - outsize = call_trans2getdfsreferral(conn,inbuf,outbuf,length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2getdfsreferral( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_get_dfs_referral); break; case TRANSACT2_IOCTL: START_PROFILE_NESTED(Trans2_ioctl); - outsize = call_trans2ioctl(conn,inbuf,outbuf,length, bufsize, - ¶ms, total_params, &data, total_data, max_data_bytes); + outsize = call_trans2ioctl( + conn, inbuf, outbuf, size, bufsize, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); END_PROFILE_NESTED(Trans2_ioctl); break; default: /* Error in request */ - DEBUG(2,("Unknown request %d in trans2 call\n", tran_call)); - SAFE_FREE(params); - SAFE_FREE(data); + DEBUG(2,("Unknown request %d in trans2 call\n", state->call)); + outsize = ERROR_DOS(ERRSRV,ERRerror); + } + return outsize; +} + +/**************************************************************************** + Reply to a SMBtrans2. + ****************************************************************************/ + +int reply_trans2(connection_struct *conn, char *inbuf,char *outbuf, + int size, int bufsize) +{ + int outsize = 0; + unsigned int dsoff = SVAL(inbuf, smb_dsoff); + unsigned int dscnt = SVAL(inbuf, smb_dscnt); + unsigned int psoff = SVAL(inbuf, smb_psoff); + unsigned int pscnt = SVAL(inbuf, smb_pscnt); + unsigned int tran_call = SVAL(inbuf, smb_setup0); + struct trans_state *state; + NTSTATUS result; + + START_PROFILE(SMBtrans2); + + if (!NT_STATUS_IS_OK(allow_new_trans(conn->pending_trans, + SVAL(inbuf, smb_mid)))) { + DEBUG(2, ("Got invalid trans2 request: %s\n", + nt_errstr(result))); END_PROFILE(SMBtrans2); - srv_signing_trans_stop(); - return ERROR_DOS(ERRSRV,ERRerror); + return ERROR_NT(result); } - - /* As we do not know how many data packets will need to be - returned here the various call_trans2xxxx calls - must send their own. Thus a call_trans2xxx routine only - returns a value other than -1 when it wants to send - an error packet. - */ - - srv_signing_trans_stop(); - SAFE_FREE(params); - SAFE_FREE(data); + if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN) + && (tran_call != TRANSACT2_GET_DFS_REFERRAL)) { + END_PROFILE(SMBtrans2); + return ERROR_DOS(ERRSRV,ERRaccess); + } + + if ((state = TALLOC_P(NULL, struct trans_state)) == NULL) { + DEBUG(0, ("talloc failed\n")); + END_PROFILE(SMBtrans2); + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + state->cmd = SMBtrans2; + + state->mid = SVAL(inbuf, smb_mid); + state->vuid = SVAL(inbuf, smb_uid); + state->setup_count = SVAL(inbuf, smb_suwcnt); + state->total_param = SVAL(inbuf, smb_tpscnt); + state->param = NULL; + state->total_data = SVAL(inbuf, smb_tdscnt); + state->data = NULL; + state->max_param_return = SVAL(inbuf, smb_mprcnt); + state->max_data_return = SVAL(inbuf, smb_mdrcnt); + state->max_setup_return = SVAL(inbuf, smb_msrcnt); + state->close_on_completion = BITSETW(inbuf+smb_vwv5,0); + state->one_way = BITSETW(inbuf+smb_vwv5,1); + + state->call = tran_call; + + /* All trans2 messages we handle have smb_sucnt == 1 - ensure this + is so as a sanity check */ + if (state->setup_count != 1) { + /* + * Need to have rc=0 for ioctl to get job id for OS/2. + * Network printing will fail if function is not successful. + * Similar function in reply.c will be used if protocol + * is LANMAN1.0 instead of LM1.2X002. + * Until DosPrintSetJobInfo with PRJINFO3 is supported, + * outbuf doesn't have to be set(only job id is used). + */ + if ( (state->setup_count == 4) && (tran_call == TRANSACT2_IOCTL) && + (SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) && + (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) { + DEBUG(2,("Got Trans2 DevIOctl jobid\n")); + } else { + DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",state->setup_count)); + DEBUG(2,("Transaction is %d\n",tran_call)); + END_PROFILE(SMBtrans2); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + } + + if ((dscnt > state->total_data) || (pscnt > state->total_param)) + goto bad_param; + + if (state->total_data) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->data = SMB_MALLOC(state->total_data); + if (state->data == NULL) { + DEBUG(0,("reply_trans2: data malloc fail for %u " + "bytes !\n", state->total_data)); + TALLOC_FREE(state); + END_PROFILE(SMBtrans2); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + if ((dsoff+dscnt < dsoff) || (dsoff+dscnt < dscnt)) + goto bad_param; + if ((smb_base(inbuf)+dsoff+dscnt > inbuf + size) || + (smb_base(inbuf)+dsoff+dscnt < smb_base(inbuf))) + goto bad_param; + + memcpy(state->data,smb_base(inbuf)+dsoff,dscnt); + } + + if (state->total_param) { + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->param = SMB_MALLOC(state->total_param); + if (state->param == NULL) { + DEBUG(0,("reply_trans: param malloc fail for %u " + "bytes !\n", state->total_param)); + SAFE_FREE(state->data); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + if ((psoff+pscnt < psoff) || (psoff+pscnt < pscnt)) + goto bad_param; + if ((smb_base(inbuf)+psoff+pscnt > inbuf + size) || + (smb_base(inbuf)+psoff+pscnt < smb_base(inbuf))) + goto bad_param; + + memcpy(state->param,smb_base(inbuf)+psoff,pscnt); + } + + state->received_data = dscnt; + state->received_param = pscnt; + + if ((state->received_param == state->total_param) && + (state->received_data == state->total_data)) { + + outsize = handle_trans2(conn, state, inbuf, outbuf, + size, bufsize); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtrans); + return outsize; + } + + DLIST_ADD(conn->pending_trans, state); + + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + show_msg(outbuf); END_PROFILE(SMBtrans2); - return outsize; /* If a correct response was needed the - call_trans2xxx calls have already sent - it. If outsize != -1 then it is returning */ + return outsize; bad_param: - srv_signing_trans_stop(); - SAFE_FREE(params); - SAFE_FREE(data); + DEBUG(0,("reply_trans2: invalid trans parameters\n")); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); END_PROFILE(SMBtrans2); return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } + + +/**************************************************************************** + Reply to a SMBtranss2 + ****************************************************************************/ + +int reply_transs2(connection_struct *conn, + char *inbuf,char *outbuf,int size,int bufsize) +{ + int outsize = 0; + unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; + struct trans_state *state; + + START_PROFILE(SMBtranss2); + + show_msg(inbuf); + + for (state = conn->pending_trans; state != NULL; + state = state->next) { + if (state->mid == SVAL(inbuf,smb_mid)) { + break; + } + } + + if ((state == NULL) || (state->cmd != SMBtrans2)) { + END_PROFILE(SMBtranss2); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + + /* Revise state->total_param and state->total_data in case they have + changed downwards */ + + if (SVAL(inbuf, smb_tpscnt) < state->total_param) + state->total_param = SVAL(inbuf, smb_tpscnt); + if (SVAL(inbuf, smb_tdscnt) < state->total_data) + state->total_data = SVAL(inbuf, smb_tdscnt); + + pcnt = SVAL(inbuf, smb_spscnt); + poff = SVAL(inbuf, smb_spsoff); + pdisp = SVAL(inbuf, smb_spsdisp); + + dcnt = SVAL(inbuf, smb_sdscnt); + doff = SVAL(inbuf, smb_sdsoff); + ddisp = SVAL(inbuf, smb_sdsdisp); + + state->received_param += pcnt; + state->received_data += dcnt; + + if ((state->received_data > state->total_data) || + (state->received_param > state->total_param)) + goto bad_param; + + if (pcnt) { + if (pdisp+pcnt > state->total_param) + goto bad_param; + if ((pdisp+pcnt < pdisp) || (pdisp+pcnt < pcnt)) + goto bad_param; + if (pdisp > state->total_param) + goto bad_param; + if ((smb_base(inbuf) + poff + pcnt > inbuf + size) || + (smb_base(inbuf) + poff + pcnt < smb_base(inbuf))) + goto bad_param; + if (state->param + pdisp < state->param) + goto bad_param; + + memcpy(state->param+pdisp,smb_base(inbuf)+poff, + pcnt); + } + + if (dcnt) { + if (ddisp+dcnt > state->total_data) + goto bad_param; + if ((ddisp+dcnt < ddisp) || (ddisp+dcnt < dcnt)) + goto bad_param; + if (ddisp > state->total_data) + goto bad_param; + if ((smb_base(inbuf) + doff + dcnt > inbuf + size) || + (smb_base(inbuf) + doff + dcnt < smb_base(inbuf))) + goto bad_param; + if (state->data + ddisp < state->data) + goto bad_param; + + memcpy(state->data+ddisp, smb_base(inbuf)+doff, + dcnt); + } + + if ((state->received_param < state->total_param) || + (state->received_data < state->total_data)) { + END_PROFILE(SMBtranss); + return -1; + } + + /* construct_reply_common has done us the favor to pre-fill the + * command field with SMBtranss2 which is wrong :-) + */ + SCVAL(outbuf,smb_com,SMBtrans2); + + outsize = handle_trans2(conn, state, inbuf, outbuf, size, bufsize); + + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + + if (outsize == 0) { + END_PROFILE(SMBtranss); + return(ERROR_DOS(ERRSRV,ERRnosupport)); + } + + END_PROFILE(SMBtranss2); + return(outsize); + + bad_param: + + DEBUG(0,("reply_transs2: invalid trans parameters\n")); + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtranss2); + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); +} diff --git a/source3/smbd/vfs-wrap.c b/source3/smbd/vfs-wrap.c index bbb7b5bb30..55bf146c20 100644 --- a/source3/smbd/vfs-wrap.c +++ b/source3/smbd/vfs-wrap.c @@ -759,11 +759,21 @@ BOOL vfswrap_lock(vfs_handle_struct *handle, files_struct *fsp, int fd, int op, BOOL result; START_PROFILE(syscall_fcntl_lock); - result = fcntl_lock(fd, op, offset, count,type); + result = fcntl_lock(fd, op, offset, count, type); END_PROFILE(syscall_fcntl_lock); return result; } +BOOL vfswrap_getlock(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype, pid_t *ppid) +{ + BOOL result; + + START_PROFILE(syscall_fcntl_getlock); + result = fcntl_getlock(fd, poffset, pcount, ptype, ppid); + END_PROFILE(syscall_fcntl_getlock); + return result; +} + int vfswrap_symlink(vfs_handle_struct *handle, connection_struct *conn, const char *oldpath, const char *newpath) { int result; diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 07e18caa5c..9a6327b33b 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -95,6 +95,7 @@ static struct vfs_ops default_vfs = { vfswrap_utime, vfswrap_ftruncate, vfswrap_lock, + vfswrap_getlock, vfswrap_symlink, vfswrap_readlink, vfswrap_link, -- cgit