[SCM] Samba Shared Repository - branch master updated - 0d7ca8e89e37d1aa07a4c8fad6a24ac41ceb4855

Volker Lendecke vlendec at samba.org
Fri Jan 16 12:01:18 GMT 2009


The branch, master has been updated
       via  0d7ca8e89e37d1aa07a4c8fad6a24ac41ceb4855 (commit)
       via  ddaa65ef6e049a185281c4d5deca4045e3b085e2 (commit)
       via  ba981128ac0ba5b6b6f865d72a26509fb52a4a3e (commit)
      from  740c5ce08138bca3c44dc3cccdd75abdb83752d5 (commit)

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 0d7ca8e89e37d1aa07a4c8fad6a24ac41ceb4855
Author: Volker Lendecke <vl at samba.org>
Date:   Fri Jan 16 11:26:05 2009 +0100

    remove the old chain_reply code

commit ddaa65ef6e049a185281c4d5deca4045e3b085e2
Author: Volker Lendecke <vl at samba.org>
Date:   Fri Nov 21 23:17:31 2008 +0100

    Correctly calculate the offset for read&x

commit ba981128ac0ba5b6b6f865d72a26509fb52a4a3e
Author: Volker Lendecke <vl at samba.org>
Date:   Mon Nov 10 10:01:26 2008 +0100

    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.

-----------------------------------------------------------------------

Summary of changes:
 source3/include/proto.h |    1 +
 source3/include/smb.h   |    9 ++
 source3/smbd/blocking.c |    6 -
 source3/smbd/pipes.c    |    8 +-
 source3/smbd/process.c  |  312 +++++++++++++++++++++++++----------------------
 source3/smbd/reply.c    |   17 ++-
 6 files changed, 196 insertions(+), 157 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source3/include/proto.h b/source3/include/proto.h
index 5d82ef0..d644b09 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -7120,6 +7120,7 @@ const char *smb_fn_name(int type);
 void add_to_common_flags2(uint32 v);
 void remove_from_common_flags2(uint32 v);
 void construct_reply_common_req(struct smb_request *req, char *outbuf);
+size_t req_wct_ofs(struct smb_request *req);
 void chain_reply(struct smb_request *req);
 void check_reload(time_t t);
 void smbd_process(void);
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 4d7d4b2..3c727ba 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 cccc5ce..9936fb2 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/pipes.c b/source3/smbd/pipes.c
index faabdd7..4841882 100644
--- a/source3/smbd/pipes.c
+++ b/source3/smbd/pipes.c
@@ -278,8 +278,12 @@ void reply_pipe_read_and_X(struct smb_request *req)
 	srv_set_message((char *)req->outbuf, 12, nread, False);
   
 	SSVAL(req->outbuf,smb_vwv5,nread);
-	SSVAL(req->outbuf,smb_vwv6,smb_offset(data,req->outbuf));
-	SSVAL(smb_buf(req->outbuf),-2,nread);
+	SSVAL(req->outbuf,smb_vwv6,
+	      req_wct_ofs(req)
+	      + 1 		/* the wct field */
+	      + 12 * sizeof(uint16_t) /* vwv */
+	      + 2);		/* the buflen field */
+	SSVAL(req->outbuf,smb_vwv11,smb_maxcnt);
   
 	DEBUG(3,("readX-IPC pnum=%04x min=%d max=%d nread=%d\n",
 		 fsp->fnum, smb_mincnt, smb_maxcnt, (int)nread));
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index 60e58aa..0c076b3 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) {
@@ -1609,212 +1610,237 @@ void construct_reply_common_req(struct smb_request *req, char *outbuf)
 	construct_reply_common(req, (char *)req->inbuf, outbuf);
 }
 
+/*
+ * How many bytes have we already accumulated up to the current wct field
+ * offset?
+ */
+
+size_t req_wct_ofs(struct smb_request *req)
+{
+	size_t buf_size;
+
+	if (req->chain_outbuf == NULL) {
+		return smb_wct - 4;
+	}
+	buf_size = talloc_get_size(req->chain_outbuf);
+	if ((buf_size % 4) != 0) {
+		buf_size += (4 - (buf_size % 4));
+	}
+	return buf_size - 4;
+}
+
+/*
+ * 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);
+}
+
 /****************************************************************************
  Construct a chained reply and add it to the already made reply
 ****************************************************************************/
 
 void chain_reply(struct smb_request *req)
 {
-	/*
-	 * Dirty little const_discard: We mess with req->inbuf, which is
-	 * declared as const. If maybe at some point this routine gets
-	 * rewritten, this const_discard could go away.
-	 */
-	char *inbuf = CONST_DISCARD(char *, req->inbuf);
-	int size = smb_len(req->inbuf)+4;
-
-	int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0);
-	unsigned smb_off2 = SVAL(inbuf,smb_vwv1);
-	char *inbuf2;
-	int outsize2;
-	int new_size;
-	char inbuf_saved[smb_wct];
-	char *outbuf = (char *)req->outbuf;
-	size_t outsize = smb_len(outbuf) + 4;
-	size_t outsize_padded;
-	size_t padding;
-	size_t ofs, to_move;
-
-	struct smb_request *req2;
-	size_t caller_outputlen;
-	char *caller_output;
-
-	/* Maybe its not chained, or it's an error packet. */
-	if (smb_com2 == 0xFF || SVAL(outbuf,smb_rcls) != 0) {
-		SCVAL(outbuf,smb_vwv0,0xFF);
-		return;
-	}
+	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 */
 
-	if (chain_size == 0) {
-		/* this is the first part of the chain */
-		orig_inbuf = inbuf;
+	uint8_t wct;
+	uint16_t *vwv;
+	uint16_t buflen;
+	uint8_t *buf;
+
+	if (IVAL(req->outbuf, smb_rcls) != 0) {
+		fixup_chain_error_packet(req);
 	}
 
 	/*
-	 * We need to save the output the caller added to the chain so that we
-	 * can splice it into the final output buffer later.
+	 * 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.
 	 */
 
-	caller_outputlen = outsize - smb_wct;
-
-	caller_output = (char *)memdup(outbuf + smb_wct, caller_outputlen);
-
-	if (caller_output == NULL) {
-		/* TODO: NT_STATUS_NO_MEMORY */
-		smb_panic("could not dup outbuf");
+	if ((req->wct < 2) || (CVAL(req->outbuf, smb_wct) < 2)) {
+		goto error;
 	}
 
 	/*
-	 * The original Win95 redirector dies on a reply to
-	 * a lockingX and read chain unless the chain reply is
-	 * 4 byte aligned. JRA.
+	 * 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.
 	 */
 
-	outsize_padded = (outsize + 3) & ~3;
-	padding = outsize_padded - outsize;
+	SCVAL(req->outbuf, smb_vwv0, 0xff);
+	SCVAL(req->outbuf, smb_vwv0+1, 0);
+	SSVAL(req->outbuf, smb_vwv1, 0);
 
-	/*
-	 * remember how much the caller added to the chain, only counting
-	 * stuff after the parameter words
-	 */
-	chain_size += (outsize_padded - smb_wct);
+	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);
+	}
 
 	/*
-	 * work out pointers into the original packets. The
-	 * headers on these need to be filled in
+	 * We use the old request's vwv field to grab the next chained command
+	 * and offset into the chained fields.
 	 */
-	inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
-
-	/* remember the original command type */
-	smb_com1 = CVAL(orig_inbuf,smb_com);
-
-	/* save the data which will be overwritten by the new headers */
-	memcpy(inbuf_saved,inbuf2,smb_wct);
 
-	/* give the new packet the same header as the last part of the SMB */
-	memmove(inbuf2,inbuf,smb_wct);
+	chain_cmd = CVAL(req->vwv+0, 0);
+	chain_offset = SVAL(req->vwv+1, 0);
 
-	/* create the in buffer */
-	SCVAL(inbuf2,smb_com,smb_com2);
-
-	/* work out the new size for the in buffer. */
-	new_size = size - (inbuf2 - inbuf);
-	if (new_size < 0) {
-		DEBUG(0,("chain_reply: chain packet size incorrect "
-			 "(orig size = %d, offset = %d)\n",
-			 size, (int)(inbuf2 - inbuf) ));
-		exit_server_cleanly("Bad chained packet");
+	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;
 	}
 
-	/* And set it in the header. */
-	smb_setlen(inbuf2, new_size - 4);
-
-	DEBUG(3,("Chained message\n"));
-	show_msg(inbuf2);
-
-	if (!(req2 = talloc(talloc_tos(), struct smb_request))) {
-		smb_panic("could not allocate smb_request");
-	}
-	init_smb_request(req2, (uint8 *)inbuf2,0, req->encrypted);
-	req2->inbuf = (uint8_t *)inbuf2;
-	req2->chain_fsp = req->chain_fsp;
-
-	/* process the request */
-	switch_message(smb_com2, req2, new_size);
-
 	/*
-	 * We don't accept deferred operations in chained requests.
+	 * 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.
 	 */
-	SMB_ASSERT(req2->outbuf != NULL);
-	outsize2 = smb_len(req2->outbuf)+4;
+
+	already_used = PTR_DIFF(req->buf+req->buflen, smb_base(req->inbuf));
+	if (chain_offset < already_used) {
+		goto error;
+	}
 
 	/*
-	 * Move away the new command output so that caller_output fits in,
-	 * copy in the caller_output saved above.
+	 * Next check: Make sure the chain offset does not point beyond the
+	 * overall smb request length.
 	 */
 
-	SMB_ASSERT(outsize_padded >= smb_wct);
+	length_needed = chain_offset+1;	/* wct */
+	if (length_needed > smblen) {
+		goto error;
+	}
 
 	/*
-	 * "ofs" is the space we need for caller_output. Equal to
-	 * caller_outputlen plus the padding.
+	 * 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.
 	 */
 
-	ofs = outsize_padded - smb_wct;
+	wct = CVAL(smb_base(req->inbuf), chain_offset);
 
 	/*
-	 * "to_move" is the amount of bytes the secondary routine gave us
+	 * Next consistency check: Make the new vwv array fits in the overall
+	 * smb request.
 	 */
 
-	to_move = outsize2 - smb_wct;
-
-	if (to_move + ofs + smb_wct + chain_size > max_send) {
-		smb_panic("replies too large -- would have to cut");
+	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);
 
 	/*
-	 * In the "new" API "outbuf" is allocated via reply_outbuf, just for
-	 * the first request in the chain. So we have to re-allocate it. In
-	 * the "old" API the only outbuf ever used is the global OutBuffer
-	 * which is always large enough.
+	 * Now grab the new byte buffer....
 	 */
 
-	outbuf = TALLOC_REALLOC_ARRAY(NULL, outbuf, char,
-				      to_move + ofs + smb_wct);
-	if (outbuf == NULL) {
-		smb_panic("could not realloc outbuf");
-	}
-
-	req->outbuf = (uint8 *)outbuf;
-
-	memmove(outbuf + smb_wct + ofs, req2->outbuf + smb_wct, to_move);
-	memcpy(outbuf + smb_wct, caller_output, caller_outputlen);
+	buflen = SVAL(vwv+wct, 0);
 
 	/*
-	 * copy the new reply header over the old one but preserve the smb_com
-	 * field
+	 * .. and check that it fits.
 	 */
-	memmove(outbuf, req2->outbuf, smb_wct);
-	SCVAL(outbuf, smb_com, smb_com1);
 
-	/*
-	 * We've just copied in the whole "wct" area from the secondary
-	 * function. Fix up the chaining: com2 and the offset need to be
-	 * readjusted.
-	 */
+	length_needed += buflen;
+	if (length_needed > smblen) {
+		goto error;
+	}
+	buf = (uint8_t *)(vwv+wct+1);
 
-	SCVAL(outbuf, smb_vwv0, smb_com2);
-	SSVAL(outbuf, smb_vwv1, chain_size + smb_wct - 4);
+	req->cmd = chain_cmd;
+	req->wct = wct;
+	req->vwv = vwv;
+	req->buflen = buflen;
+	req->buf = buf;
 
-	if (padding != 0) {
+	switch_message(chain_cmd, req, smblen);
 
+	if (req->outbuf == NULL) {
 		/*
-		 * Due to padding we have some uninitialized bytes after the
-		 * caller's output
+		 * This happens if the chained command has suspended itself or
+		 * if it has called srv_send_smb() itself.
 		 */
-
-		memset(outbuf + outsize, 0, padding);
+		return;
 	}
 
-	smb_setlen(outbuf, outsize2 + caller_outputlen + padding - 4);
-
 	/*
-	 * restore the saved data, being careful not to overwrite any data
-	 * from the reply header
+	 * 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.
 	 */
-	memcpy(inbuf2,inbuf_saved,smb_wct);
-
-	SAFE_FREE(caller_output);
-	TALLOC_FREE(req2);
+	goto done;
 
+ error:
 	/*
-	 * Reset the chain_size for our caller's offset calculations
+	 * 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);
 
-	chain_size -= (outsize_padded - smb_wct);
+	smb_setlen((char *)(req->chain_outbuf),
+		   talloc_get_size(req->chain_outbuf) - 4);
 
-	return;
+	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.");
+	}
 }
 
 /****************************************************************************
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index d770496..2883614 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -3169,7 +3169,8 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n",
  Setup readX header.
 ****************************************************************************/
 
-static int setup_readX_header(char *outbuf, size_t smb_maxcnt)
+static int setup_readX_header(struct smb_request *req, char *outbuf,
+			      size_t smb_maxcnt)
 {
 	int outsize;
 	char *data;
@@ -3182,9 +3183,13 @@ static int setup_readX_header(char *outbuf, size_t smb_maxcnt)
 	SCVAL(outbuf,smb_vwv0,0xFF);
 	SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */
 	SSVAL(outbuf,smb_vwv5,smb_maxcnt);
-	SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+	SSVAL(outbuf,smb_vwv6,
+	      req_wct_ofs(req)
+	      + 1 		/* the wct field */
+	      + 12 * sizeof(uint16_t) /* vwv */
+	      + 2);		/* the buflen field */
 	SSVAL(outbuf,smb_vwv7,(smb_maxcnt >> 16));
-	SSVAL(smb_buf(outbuf),-2,smb_maxcnt);
+	SSVAL(outbuf,smb_vwv11,smb_maxcnt);
 	/* Reset the outgoing length, set_message truncates at 0x1FFFF. */
 	_smb_setlen_large(outbuf,(smb_size + 12*2 + smb_maxcnt - 4));
 	return outsize;
@@ -3238,7 +3243,7 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req,
 		header = data_blob_const(headerbuf, sizeof(headerbuf));
 
 		construct_reply_common_req(req, (char *)headerbuf);
-		setup_readX_header((char *)headerbuf, smb_maxcnt);
+		setup_readX_header(req, (char *)headerbuf, smb_maxcnt);
 
 		if ((nread = SMB_VFS_SENDFILE(smbd_server_fd(), fsp, &header, startpos, smb_maxcnt)) == -1) {


-- 
Samba Shared Repository


More information about the samba-cvs mailing list