summaryrefslogtreecommitdiff
path: root/source3
diff options
context:
space:
mode:
Diffstat (limited to 'source3')
-rw-r--r--source3/include/smb.h9
-rw-r--r--source3/smbd/blocking.c6
-rw-r--r--source3/smbd/process.c217
3 files changed, 226 insertions, 6 deletions
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 4d7d4b2f38..3c727bafbf 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -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. */
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.
****************************************************************************/