mirror of
https://github.com/samba-team/samba.git
synced 2025-03-05 20:58:40 +03:00
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.
This commit is contained in:
parent
740c5ce081
commit
ba981128ac
@ -627,7 +627,16 @@ struct smb_request {
|
||||
size_t unread_bytes;
|
||||
bool encrypted;
|
||||
connection_struct *conn;
|
||||
|
||||
/*
|
||||
* Chained request handling
|
||||
*/
|
||||
struct files_struct *chain_fsp;
|
||||
|
||||
/*
|
||||
* Here we collect the outbufs from the chain handlers
|
||||
*/
|
||||
uint8_t *chain_outbuf;
|
||||
};
|
||||
|
||||
/* Defines for the sent_oplock_break field above. */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
****************************************************************************/
|
||||
|
Loading…
x
Reference in New Issue
Block a user