summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2012-02-28 00:19:48 +0100
committerVolker Lendecke <vl@samba.org>2012-03-10 15:34:11 +0100
commitc9870a62f5d8865e67ae519db3f8e890afb6ee70 (patch)
treeffdad08bf8cb9962d01a0d07abfaf1cd45d9ee73 /source3/smbd
parent90b33a05e967e9e29c4584bed188ef6fa5a3fbf0 (diff)
downloadsamba-c9870a62f5d8865e67ae519db3f8e890afb6ee70.tar.gz
samba-c9870a62f5d8865e67ae519db3f8e890afb6ee70.tar.bz2
samba-c9870a62f5d8865e67ae519db3f8e890afb6ee70.zip
s3: Add a new set of andx chain handling routines
This is in preparation of getting rid of chain_reply.
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/process.c236
-rw-r--r--source3/smbd/proto.h12
2 files changed, 248 insertions, 0 deletions
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index f87eccfdcd..b83e3ddcde 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -1880,6 +1880,242 @@ static bool smb_splice_chain(uint8_t **poutbuf, const uint8_t *andx_buf)
return true;
}
+bool smb1_is_chain(const uint8_t *buf)
+{
+ uint8_t cmd, wct, andx_cmd;
+
+ cmd = CVAL(buf, smb_com);
+ if (!is_andx_req(cmd)) {
+ return false;
+ }
+ wct = CVAL(buf, smb_wct);
+ if (wct < 2) {
+ return false;
+ }
+ andx_cmd = CVAL(buf, smb_vwv);
+ return (andx_cmd != 0xFF);
+}
+
+bool smb1_walk_chain(const uint8_t *buf,
+ bool (*fn)(uint8_t cmd,
+ uint8_t wct, const uint16_t *vwv,
+ uint16_t num_bytes, const uint8_t *bytes,
+ void *private_data),
+ void *private_data)
+{
+ size_t smblen = smb_len(buf);
+ const char *smb_buf = smb_base(buf);
+ uint8_t cmd, chain_cmd;
+ uint8_t wct;
+ const uint16_t *vwv;
+ uint16_t num_bytes;
+ const uint8_t *bytes;
+
+ cmd = CVAL(buf, smb_com);
+ wct = CVAL(buf, smb_wct);
+ vwv = (const uint16_t *)(buf + smb_vwv);
+ num_bytes = smb_buflen(buf);
+ bytes = (uint8_t *)smb_buf_const(buf);
+
+ if (!fn(cmd, wct, vwv, num_bytes, bytes, private_data)) {
+ return false;
+ }
+
+ if (!is_andx_req(cmd)) {
+ return true;
+ }
+ if (wct < 2) {
+ return false;
+ }
+
+ chain_cmd = CVAL(vwv, 0);
+
+ while (chain_cmd != 0xff) {
+ uint32_t chain_offset; /* uint32_t to avoid overflow */
+ size_t length_needed;
+ ptrdiff_t vwv_offset;
+
+ chain_offset = SVAL(vwv+1, 0);
+
+ /*
+ * Check if the client tries to fool us. The chain
+ * offset needs to point beyond the current request in
+ * the chain, it needs to strictly grow. Otherwise we
+ * might be tricked into an endless loop always
+ * processing the same request over and over again. We
+ * used to assume that vwv and the byte buffer array
+ * in a chain are always attached, but OS/2 the
+ * Write&X/Read&X chain puts the Read&X vwv array
+ * right behind the Write&X vwv chain. The Write&X bcc
+ * array is put behind the Read&X vwv array. So now we
+ * check whether the chain offset points strictly
+ * behind the previous vwv array. req->buf points
+ * right after the vwv array of the previous
+ * request. See
+ * https://bugzilla.samba.org/show_bug.cgi?id=8360 for
+ * more information.
+ */
+
+ vwv_offset = ((const char *)vwv - smb_buf);
+ if (chain_offset <= vwv_offset) {
+ return false;
+ }
+
+ /*
+ * 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) {
+ return false;
+ }
+
+ /*
+ * Now comes the pointer magic. Goal here is to set up
+ * vwv and buf correctly again. The chain offset (the
+ * former vwv[1]) points at the new wct field.
+ */
+
+ wct = CVAL(smb_buf, chain_offset);
+
+ if (is_andx_req(chain_cmd) && (wct < 2)) {
+ return false;
+ }
+
+ /*
+ * 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) {
+ return false;
+ }
+ vwv = (const uint16_t *)(smb_buf + chain_offset + 1);
+
+ /*
+ * Now grab the new byte buffer....
+ */
+
+ num_bytes = SVAL(vwv+wct, 0);
+
+ /*
+ * .. and check that it fits.
+ */
+
+ length_needed += num_bytes;
+ if (length_needed > smblen) {
+ return false;
+ }
+ bytes = (const uint8_t *)(vwv+wct+1);
+
+ if (!fn(chain_cmd, wct, vwv, num_bytes, bytes, private_data)) {
+ return false;
+ }
+
+ if (!is_andx_req(chain_cmd)) {
+ return true;
+ }
+ chain_cmd = CVAL(vwv, 0);
+ }
+ return true;
+}
+
+static bool smb1_chain_length_cb(uint8_t cmd,
+ uint8_t wct, const uint16_t *vwv,
+ uint16_t num_bytes, const uint8_t *bytes,
+ void *private_data)
+{
+ unsigned *count = (unsigned *)private_data;
+ *count += 1;
+ return true;
+}
+
+unsigned smb1_chain_length(const uint8_t *buf)
+{
+ unsigned count = 0;
+
+ if (!smb1_walk_chain(buf, smb1_chain_length_cb, &count)) {
+ return 0;
+ }
+ return count;
+}
+
+struct smb1_parse_chain_state {
+ TALLOC_CTX *mem_ctx;
+ const uint8_t *buf;
+ struct smbd_server_connection *sconn;
+ bool encrypted;
+ uint32_t seqnum;
+
+ struct smb_request **reqs;
+ unsigned num_reqs;
+};
+
+static bool smb1_parse_chain_cb(uint8_t cmd,
+ uint8_t wct, const uint16_t *vwv,
+ uint16_t num_bytes, const uint8_t *bytes,
+ void *private_data)
+{
+ struct smb1_parse_chain_state *state =
+ (struct smb1_parse_chain_state *)private_data;
+ struct smb_request **reqs;
+ struct smb_request *req;
+ bool ok;
+
+ reqs = talloc_realloc(state->mem_ctx, state->reqs,
+ struct smb_request *, state->num_reqs+1);
+ if (reqs == NULL) {
+ return false;
+ }
+ state->reqs = reqs;
+
+ req = talloc(reqs, struct smb_request);
+ if (req == NULL) {
+ return false;
+ }
+
+ ok = init_smb_request(req, state->sconn, state->buf, 0,
+ state->encrypted, state->seqnum);
+ if (!ok) {
+ return false;
+ }
+ req->cmd = cmd;
+ req->wct = wct;
+ req->vwv = vwv;
+ req->buflen = num_bytes;
+ req->buf = bytes;
+
+ reqs[state->num_reqs] = req;
+ state->num_reqs += 1;
+ return true;
+}
+
+bool smb1_parse_chain(TALLOC_CTX *mem_ctx, const uint8_t *buf,
+ struct smbd_server_connection *sconn,
+ bool encrypted, uint32_t seqnum,
+ struct smb_request ***reqs, unsigned *num_reqs)
+{
+ struct smb1_parse_chain_state state;
+
+ state.mem_ctx = mem_ctx;
+ state.buf = buf;
+ state.sconn = sconn;
+ state.encrypted = encrypted;
+ state.seqnum = seqnum;
+ state.reqs = NULL;
+ state.num_reqs = 0;
+
+ if (!smb1_walk_chain(buf, smb1_parse_chain_cb, &state)) {
+ TALLOC_FREE(state.reqs);
+ return false;
+ }
+ *reqs = state.reqs;
+ *num_reqs = state.num_reqs;
+ return true;
+}
+
/****************************************************************************
Construct a chained reply and add it to the already made reply
****************************************************************************/
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index e97a85d589..e5dd762663 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -785,6 +785,18 @@ 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);
+bool smb1_is_chain(const uint8_t *buf);
+bool smb1_walk_chain(const uint8_t *buf,
+ bool (*fn)(uint8_t cmd,
+ uint8_t wct, const uint16_t *vwv,
+ uint16_t num_bytes, const uint8_t *bytes,
+ void *private_data),
+ void *private_data);
+unsigned smb1_chain_length(const uint8_t *buf);
+bool smb1_parse_chain(TALLOC_CTX *mem_ctx, const uint8_t *buf,
+ struct smbd_server_connection *sconn,
+ bool encrypted, uint32_t seqnum,
+ struct smb_request ***reqs, unsigned *num_reqs);
void chain_reply(struct smb_request *req);
bool req_is_in_chain(struct smb_request *req);
void smbd_process(struct tevent_context *ev_ctx,