diff options
author | Stefan Metzmacher <metze@samba.org> | 2011-11-16 11:20:15 +0100 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2011-11-24 19:02:30 +0100 |
commit | 7c5651c3f63b29e689c9e07a33bec751a5e49ac7 (patch) | |
tree | 5a0bfb5f2d5756a1c21dc7ce745c2b2e3dc2129b | |
parent | 94cb738dd4deab7dc97e0da77a406264297713b9 (diff) | |
download | samba-7c5651c3f63b29e689c9e07a33bec751a5e49ac7.tar.gz samba-7c5651c3f63b29e689c9e07a33bec751a5e49ac7.tar.xz samba-7c5651c3f63b29e689c9e07a33bec751a5e49ac7.zip |
smbXcli: add smb1cli_inbuf_parse_chain()
metze
-rw-r--r-- | libcli/smb/smbXcli_base.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index ce6e3242e97..d53be9b6f2f 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -1164,6 +1164,180 @@ static void smbXcli_conn_received(struct tevent_req *subreq) } } +static NTSTATUS smb1cli_inbuf_parse_chain(uint8_t *buf, TALLOC_CTX *mem_ctx, + struct iovec **piov, int *pnum_iov) +{ + struct iovec *iov; + int num_iov; + size_t buflen; + size_t taken; + size_t remaining; + uint8_t *hdr; + uint8_t cmd; + uint32_t wct_ofs; + + buflen = smb_len_nbt(buf); + taken = 0; + + hdr = buf + NBT_HDR_SIZE; + + if (buflen < MIN_SMB_SIZE) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + /* + * This returns iovec elements in the following order: + * + * - SMB header + * + * - Parameter Block + * - Data Block + * + * - Parameter Block + * - Data Block + * + * - Parameter Block + * - Data Block + */ + num_iov = 1; + + iov = talloc_array(mem_ctx, struct iovec, num_iov); + if (iov == NULL) { + return NT_STATUS_NO_MEMORY; + } + iov[0].iov_base = hdr; + iov[0].iov_len = HDR_WCT; + taken += HDR_WCT; + + cmd = CVAL(hdr, HDR_COM); + wct_ofs = HDR_WCT; + + while (true) { + size_t len = buflen - taken; + struct iovec *cur; + struct iovec *iov_tmp; + uint8_t wct; + uint32_t bcc_ofs; + uint16_t bcc; + size_t needed; + + /* + * we need at least WCT and BCC + */ + needed = sizeof(uint8_t) + sizeof(uint16_t); + if (len < needed) { + DEBUG(10, ("%s: %d bytes left, expected at least %d\n", + __location__, (int)len, (int)needed)); + goto inval; + } + + /* + * Now we check if the specified words are there + */ + wct = CVAL(hdr, wct_ofs); + needed += wct * sizeof(uint16_t); + if (len < needed) { + DEBUG(10, ("%s: %d bytes left, expected at least %d\n", + __location__, (int)len, (int)needed)); + goto inval; + } + + /* + * Now we check if the specified bytes are there + */ + bcc_ofs = wct_ofs + sizeof(uint8_t) + wct * sizeof(uint16_t); + bcc = SVAL(hdr, bcc_ofs); + needed += bcc * sizeof(uint8_t); + if (len < needed) { + DEBUG(10, ("%s: %d bytes left, expected at least %d\n", + __location__, (int)len, (int)needed)); + goto inval; + } + + /* + * we allocate 2 iovec structures for words and bytes + */ + iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec, + num_iov + 2); + if (iov_tmp == NULL) { + TALLOC_FREE(iov); + return NT_STATUS_NO_MEMORY; + } + iov = iov_tmp; + cur = &iov[num_iov]; + num_iov += 2; + + cur[0].iov_len = wct * sizeof(uint16_t); + cur[0].iov_base = hdr + (wct_ofs + sizeof(uint8_t)); + cur[1].iov_len = bcc * sizeof(uint8_t); + cur[1].iov_base = hdr + (bcc_ofs + sizeof(uint16_t)); + + taken += needed; + + if (!smb1cli_is_andx_req(cmd)) { + /* + * If the current command does not have AndX chanining + * we are done. + */ + break; + } + + if (wct == 0 && bcc == 0) { + /* + * An empty response also ends the chain, + * most likely with an error. + */ + break; + } + + if (wct < 2) { + DEBUG(10, ("%s: wct[%d] < 2 for cmd[0x%02X]\n", + __location__, (int)wct, (int)cmd)); + goto inval; + } + cmd = CVAL(cur[0].iov_base, 0); + if (cmd == 0xFF) { + /* + * If it is the end of the chain we are also done. + */ + break; + } + wct_ofs = SVAL(cur[0].iov_base, 2); + + if (wct_ofs < taken) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (wct_ofs > buflen) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + /* + * we consumed everything up to the start of the next + * parameter block. + */ + taken = wct_ofs; + } + + remaining = buflen - taken; + + if (remaining > 0 && num_iov >= 3) { + /* + * The last DATA block gets the remaining + * bytes, this is needed to support + * CAP_LARGE_WRITEX and CAP_LARGE_READX. + */ + iov[num_iov-1].iov_len += remaining; + } + + *piov = iov; + *pnum_iov = num_iov; + return NT_STATUS_OK; + +inval: + TALLOC_FREE(iov); + return NT_STATUS_INVALID_NETWORK_RESPONSE; +} + static NTSTATUS smb1cli_conn_dispatch_incoming(struct smbXcli_conn *conn, TALLOC_CTX *tmp_mem, uint8_t *inbuf) |