diff options
author | Volker Lendecke <vl@samba.org> | 2008-11-10 10:01:26 +0100 |
---|---|---|
committer | Volker Lendecke <vl@samba.org> | 2009-01-16 13:00:28 +0100 |
commit | ba981128ac0ba5b6b6f865d72a26509fb52a4a3e (patch) | |
tree | 2a3ddf28bf25e0cd63af1fed77004d0feff77ad1 /source3/smbd | |
parent | 740c5ce08138bca3c44dc3cccdd75abdb83752d5 (diff) | |
download | samba-ba981128ac0ba5b6b6f865d72a26509fb52a4a3e.tar.gz samba-ba981128ac0ba5b6b6f865d72a26509fb52a4a3e.tar.bz2 samba-ba981128ac0ba5b6b6f865d72a26509fb52a4a3e.zip |
Add a new implementation of chain_reply
This the global variable "orig_inbuf" in the old chain_reply code. This global
variable was one of the reasons why we had the silly restriction to not allow
async requests within a request chain.
Diffstat (limited to 'source3/smbd')
-rw-r--r-- | source3/smbd/blocking.c | 6 | ||||
-rw-r--r-- | source3/smbd/process.c | 217 |
2 files changed, 217 insertions, 6 deletions
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index cccc5ce727..9936fb219f 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -238,12 +238,6 @@ static void reply_lockingX_success(blocking_lock_record *blr) */ chain_reply(blr->req); - - if (!srv_send_smb(smbd_server_fd(), (char *)blr->req->outbuf, - IS_CONN_ENCRYPTED(blr->fsp->conn))) { - exit_server_cleanly("send_blocking_reply: srv_send_smb failed."); - } - TALLOC_FREE(blr->req->outbuf); } diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 60e58aac22..3547bfcc3a 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -372,6 +372,7 @@ void init_smb_request(struct smb_request *req, req->encrypted = encrypted; req->conn = conn_find(req->tid); req->chain_fsp = NULL; + req->chain_outbuf = NULL; /* Ensure we have at least wct words and 2 bytes of bcc. */ if (smb_size + req->wct*2 > req_size) { @@ -1613,6 +1614,8 @@ void construct_reply_common_req(struct smb_request *req, char *outbuf) Construct a chained reply and add it to the already made reply ****************************************************************************/ +#if 0 + void chain_reply(struct smb_request *req) { /* @@ -1817,6 +1820,220 @@ void chain_reply(struct smb_request *req) return; } +#else + +/* + * Hack around reply_nterror & friends not being aware of chained requests, + * generating illegal (i.e. wct==0) chain replies. + */ + +static void fixup_chain_error_packet(struct smb_request *req) +{ + uint8_t *outbuf = req->outbuf; + req->outbuf = NULL; + reply_outbuf(req, 2, 0); + memcpy(req->outbuf, outbuf, smb_wct); + TALLOC_FREE(outbuf); + SCVAL(req->outbuf, smb_vwv0, 0xff); +} + +void chain_reply(struct smb_request *req) +{ + size_t smblen = smb_len(req->inbuf); + size_t already_used, length_needed; + uint8_t chain_cmd; + uint32_t chain_offset; /* uint32_t to avoid overflow */ + + uint8_t wct; + uint16_t *vwv; + uint16_t buflen; + uint8_t *buf; + + if (IVAL(req->outbuf, smb_rcls) != 0) { + fixup_chain_error_packet(req); + } + + /* + * Any of the AndX requests and replies have at least a wct of + * 2. vwv[0] is the next command, vwv[1] is the offset from the + * beginning of the SMB header to the next wct field. + * + * None of the AndX requests put anything valuable in vwv[0] and [1], + * so we can overwrite it here to form the chain. + */ + + if ((req->wct < 2) || (CVAL(req->outbuf, smb_wct) < 2)) { + goto error; + } + + /* + * Here we assume that this is the end of the chain. For that we need + * to set "next command" to 0xff and the offset to 0. If we later find + * more commands in the chain, this will be overwritten again. + */ + + SCVAL(req->outbuf, smb_vwv0, 0xff); + SCVAL(req->outbuf, smb_vwv0+1, 0); + SSVAL(req->outbuf, smb_vwv1, 0); + + if (req->chain_outbuf == NULL) { + /* + * In req->chain_outbuf we collect all the replies. Start the + * chain by copying in the first reply. + */ + req->chain_outbuf = req->outbuf; + req->outbuf = NULL; + } else { + if (!smb_splice_chain(&req->chain_outbuf, + CVAL(req->outbuf, smb_com), + CVAL(req->outbuf, smb_wct), + (uint16_t *)(req->outbuf + smb_vwv), + 0, smb_buflen(req->outbuf), + (uint8_t *)smb_buf(req->outbuf))) { + goto error; + } + TALLOC_FREE(req->outbuf); + } + + /* + * We use the old request's vwv field to grab the next chained command + * and offset into the chained fields. + */ + + chain_cmd = CVAL(req->vwv+0, 0); + chain_offset = SVAL(req->vwv+1, 0); + + if (chain_cmd == 0xff) { + /* + * End of chain, no more requests from the client. So ship the + * replies. + */ + smb_setlen((char *)(req->chain_outbuf), + talloc_get_size(req->chain_outbuf) - 4); + if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf, + IS_CONN_ENCRYPTED(req->conn) + ||req->encrypted)) { + exit_server_cleanly("chain_reply: srv_send_smb " + "failed."); + } + return; + } + + /* + * Check if the client tries to fool us. The request so far uses the + * space to the end of the byte buffer in the request just + * processed. The chain_offset can't point into that area. If that was + * the case, we could end up with an endless processing of the chain, + * we would always handle the same request. + */ + + already_used = PTR_DIFF(req->buf+req->buflen, smb_base(req->inbuf)); + if (chain_offset < already_used) { + goto error; + } + + /* + * Next check: Make sure the chain offset does not point beyond the + * overall smb request length. + */ + + length_needed = chain_offset+1; /* wct */ + if (length_needed > smblen) { + goto error; + } + + /* + * Now comes the pointer magic. Goal here is to set up req->vwv and + * req->buf correctly again to be able to call the subsequent + * switch_message(). The chain offset (the former vwv[1]) points at + * the new wct field. + */ + + wct = CVAL(smb_base(req->inbuf), chain_offset); + + /* + * Next consistency check: Make the new vwv array fits in the overall + * smb request. + */ + + length_needed += (wct+1)*sizeof(uint16_t); /* vwv+buflen */ + if (length_needed > smblen) { + goto error; + } + vwv = (uint16_t *)(smb_base(req->inbuf) + chain_offset + 1); + + /* + * Now grab the new byte buffer.... + */ + + buflen = SVAL(vwv+wct, 0); + + /* + * .. and check that it fits. + */ + + length_needed += buflen; + if (length_needed > smblen) { + goto error; + } + buf = (uint8_t *)(vwv+wct+1); + + req->cmd = chain_cmd; + req->wct = wct; + req->vwv = vwv; + req->buflen = buflen; + req->buf = buf; + + switch_message(chain_cmd, req, smblen); + + if (req->outbuf == NULL) { + /* + * This happens if the chained command has suspended itself or + * if it has called srv_send_smb() itself. + */ + return; + } + + /* + * We end up here if the chained command was not itself chained or + * suspended, but for example a close() command. We now need to splice + * the chained commands' outbuf into the already built up chain_outbuf + * and ship the result. + */ + goto done; + + error: + /* + * We end up here if there's any error in the chain syntax. Report a + * DOS error, just like Windows does. + */ + reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRerror)); + fixup_chain_error_packet(req); + + done: + if (!smb_splice_chain(&req->chain_outbuf, + CVAL(req->outbuf, smb_com), + CVAL(req->outbuf, smb_wct), + (uint16_t *)(req->outbuf + smb_vwv), + 0, smb_buflen(req->outbuf), + (uint8_t *)smb_buf(req->outbuf))) { + exit_server_cleanly("chain_reply: smb_splice_chain failed\n"); + } + TALLOC_FREE(req->outbuf); + + smb_setlen((char *)(req->chain_outbuf), + talloc_get_size(req->chain_outbuf) - 4); + + show_msg((char *)(req->chain_outbuf)); + + if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf, + IS_CONN_ENCRYPTED(req->conn)||req->encrypted)) { + exit_server_cleanly("construct_reply: srv_send_smb failed."); + } +} + +#endif + /**************************************************************************** Check if services need reloading. ****************************************************************************/ |