diff options
author | Jeremy Allison <jra@samba.org> | 2006-04-10 15:33:04 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 11:15:57 -0500 |
commit | 22dbd67708f1651a2341d70ce576fac360affccf (patch) | |
tree | 83b1bb601be996716bd4e8d3ae79e94df056df2e /source3/smbd/ipc.c | |
parent | 0f985dcb1978afd4b383116f01b3acf65c39ecd7 (diff) | |
download | samba-22dbd67708f1651a2341d70ce576fac360affccf.tar.gz samba-22dbd67708f1651a2341d70ce576fac360affccf.tar.xz samba-22dbd67708f1651a2341d70ce576fac360affccf.zip |
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)
Diffstat (limited to 'source3/smbd/ipc.c')
-rw-r--r-- | source3/smbd/ipc.c | 452 |
1 files changed, 271 insertions, 181 deletions
diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index 427b6ae2144..1b5a5f39c72 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;i<suwcnt;i++) - setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD); + for (i=0;i<state->setup_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); } |