mirror of
https://github.com/samba-team/samba.git
synced 2024-12-25 23:21:54 +03:00
r2180: added RPC flags "padcheck" which enables checking of all received pad
bytes to make sure they are zero. Non-zero values usually indicate one of two things: - the server is leaking data through sending uninitialised memory - we have mistaken a real field in the IDL for padding to differentiate between the two you really need to run with "print,padcheck" and look carefully at whether the non-zero pad bytes are random or appear to be deliberate.
This commit is contained in:
parent
25f85efd75
commit
7fdb778f81
@ -42,7 +42,7 @@ struct ndr_token_list {
|
||||
*/
|
||||
struct ndr_pull {
|
||||
uint32_t flags; /* LIBNDR_FLAG_* */
|
||||
char *data;
|
||||
uint8_t *data;
|
||||
uint32_t data_size;
|
||||
uint32_t offset;
|
||||
|
||||
@ -62,7 +62,7 @@ struct ndr_pull_save {
|
||||
/* structure passed to functions that generate NDR formatted data */
|
||||
struct ndr_push {
|
||||
uint32_t flags; /* LIBNDR_FLAG_* */
|
||||
char *data;
|
||||
uint8_t *data;
|
||||
uint32_t alloc_size;
|
||||
uint32_t offset;
|
||||
|
||||
@ -112,6 +112,9 @@ struct ndr_print {
|
||||
/* used to force a section of IDL to be little-endian */
|
||||
#define LIBNDR_FLAG_LITTLE_ENDIAN (1<<17)
|
||||
|
||||
/* used to check if alignment padding is zero */
|
||||
#define LIBNDR_FLAG_PAD_CHECK (1<<18)
|
||||
|
||||
|
||||
/* useful macro for debugging */
|
||||
#define NDR_PRINT_DEBUG(type, p) ndr_print_debug((ndr_print_fn_t)ndr_print_ ##type, #p, p)
|
||||
@ -161,6 +164,9 @@ enum ndr_err_code {
|
||||
|
||||
#define NDR_PULL_ALIGN(ndr, n) do { \
|
||||
if (!(ndr->flags & LIBNDR_FLAG_NOALIGN)) { \
|
||||
if (ndr->flags & LIBNDR_FLAG_PAD_CHECK) { \
|
||||
ndr_check_padding(ndr, n); \
|
||||
} \
|
||||
ndr->offset = (ndr->offset + (n-1)) & ~(n-1); \
|
||||
} \
|
||||
if (ndr->offset >= ndr->data_size) { \
|
||||
|
@ -28,6 +28,31 @@
|
||||
#define NDR_SSVAL(ndr, ofs, v) do { if (NDR_BE(ndr)) { RSSVAL(ndr->data,ofs,v); } else SSVAL(ndr->data,ofs,v); } while (0)
|
||||
#define NDR_SIVAL(ndr, ofs, v) do { if (NDR_BE(ndr)) { RSIVAL(ndr->data,ofs,v); } else SIVAL(ndr->data,ofs,v); } while (0)
|
||||
|
||||
|
||||
/*
|
||||
check for data leaks from the server by looking for non-zero pad bytes
|
||||
these could also indicate that real structure elements have been
|
||||
mistaken for padding in the IDL
|
||||
*/
|
||||
void ndr_check_padding(struct ndr_pull *ndr, size_t n)
|
||||
{
|
||||
size_t ofs2 = (ndr->offset + (n-1)) & ~(n-1);
|
||||
int i;
|
||||
for (i=ndr->offset;i<ofs2;i++) {
|
||||
if (ndr->data[i] != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i<ofs2) {
|
||||
DEBUG(0,("WARNING: Non-zero padding to %d: ", n));
|
||||
for (i=ndr->offset;i<ofs2;i++) {
|
||||
DEBUG(0,("%02x ", ndr->data[i]));
|
||||
}
|
||||
DEBUG(0,("\n"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
parse a uint8
|
||||
*/
|
||||
|
@ -102,16 +102,33 @@ void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
setup for a ndr pull, also setting up any flags from the binding string
|
||||
*/
|
||||
static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_pipe *p, DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx);
|
||||
|
||||
if (ndr == NULL) return ndr;
|
||||
|
||||
if (p->flags & DCERPC_DEBUG_PAD_CHECK) {
|
||||
ndr->flags |= LIBNDR_FLAG_PAD_CHECK;
|
||||
}
|
||||
|
||||
return ndr;
|
||||
}
|
||||
|
||||
/*
|
||||
parse a data blob into a dcerpc_packet structure. This handles both
|
||||
input and output packets
|
||||
*/
|
||||
static NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
|
||||
static NTSTATUS dcerpc_pull(struct dcerpc_pipe *p, DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
|
||||
struct dcerpc_packet *pkt)
|
||||
{
|
||||
struct ndr_pull *ndr;
|
||||
|
||||
ndr = ndr_pull_init_blob(blob, mem_ctx);
|
||||
ndr = ndr_pull_init_flags(p, blob, mem_ctx);
|
||||
if (!ndr) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
@ -137,10 +154,10 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
|
||||
|
||||
/* non-signed packets are simpler */
|
||||
if (!p->security_state.auth_info || !p->security_state.generic_state) {
|
||||
return dcerpc_pull(blob, mem_ctx, pkt);
|
||||
return dcerpc_pull(p, blob, mem_ctx, pkt);
|
||||
}
|
||||
|
||||
ndr = ndr_pull_init_blob(blob, mem_ctx);
|
||||
ndr = ndr_pull_init_flags(p, blob, mem_ctx);
|
||||
if (!ndr) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
@ -172,7 +189,7 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
|
||||
pkt->u.response.stub_and_verifier.length -= auth_blob.length;
|
||||
|
||||
/* pull the auth structure */
|
||||
ndr = ndr_pull_init_blob(&auth_blob, mem_ctx);
|
||||
ndr = ndr_pull_init_flags(p, &auth_blob, mem_ctx);
|
||||
if (!ndr) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
@ -439,7 +456,7 @@ NTSTATUS dcerpc_bind(struct dcerpc_pipe *p,
|
||||
}
|
||||
|
||||
/* unmarshall the NDR */
|
||||
status = dcerpc_pull(&blob, mem_ctx, &pkt);
|
||||
status = dcerpc_pull(p, &blob, mem_ctx, &pkt);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
@ -513,7 +530,7 @@ NTSTATUS dcerpc_alter(struct dcerpc_pipe *p,
|
||||
}
|
||||
|
||||
/* unmarshall the NDR */
|
||||
status = dcerpc_pull(&blob, mem_ctx, &pkt);
|
||||
status = dcerpc_pull(p, &blob, mem_ctx, &pkt);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
@ -855,7 +872,8 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
|
||||
for that to the NDR we initially generated. If they don't match then we know
|
||||
we must have a bug in either the pull or push side of our code
|
||||
*/
|
||||
static NTSTATUS dcerpc_ndr_validate_in(TALLOC_CTX *mem_ctx,
|
||||
static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_pipe *p,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB blob,
|
||||
size_t struct_size,
|
||||
NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
|
||||
@ -872,7 +890,7 @@ static NTSTATUS dcerpc_ndr_validate_in(TALLOC_CTX *mem_ctx,
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
pull = ndr_pull_init_blob(&blob, mem_ctx);
|
||||
pull = ndr_pull_init_flags(p, &blob, mem_ctx);
|
||||
if (!pull) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
@ -918,7 +936,8 @@ static NTSTATUS dcerpc_ndr_validate_in(TALLOC_CTX *mem_ctx,
|
||||
initially generated. If they don't match then we know we must have a
|
||||
bug in either the pull or push side of our code
|
||||
*/
|
||||
static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
|
||||
static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_pipe *p,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
void *struct_ptr,
|
||||
size_t struct_size,
|
||||
NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
|
||||
@ -950,7 +969,7 @@ static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
|
||||
|
||||
blob = ndr_push_blob(push);
|
||||
|
||||
pull = ndr_pull_init_blob(&blob, mem_ctx);
|
||||
pull = ndr_pull_init_flags(p, &blob, mem_ctx);
|
||||
if (!pull) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
@ -1032,7 +1051,7 @@ struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
|
||||
request = ndr_push_blob(push);
|
||||
|
||||
if (p->flags & DCERPC_DEBUG_VALIDATE_IN) {
|
||||
status = dcerpc_ndr_validate_in(mem_ctx, request, struct_size,
|
||||
status = dcerpc_ndr_validate_in(p, mem_ctx, request, struct_size,
|
||||
ndr_push, ndr_pull);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(2,("Validation failed in dcerpc_ndr_request_send - %s\n",
|
||||
@ -1086,7 +1105,7 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
|
||||
talloc_free(req);
|
||||
|
||||
/* prepare for ndr_pull_* */
|
||||
pull = ndr_pull_init_blob(&response, ndr.mem_ctx);
|
||||
pull = ndr_pull_init_flags(p, &response, ndr.mem_ctx);
|
||||
if (!pull) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
@ -1105,7 +1124,7 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
|
||||
}
|
||||
|
||||
if (p->flags & DCERPC_DEBUG_VALIDATE_OUT) {
|
||||
status = dcerpc_ndr_validate_out(ndr.mem_ctx, ndr.struct_ptr, ndr.struct_size,
|
||||
status = dcerpc_ndr_validate_out(p, ndr.mem_ctx, ndr.struct_ptr, ndr.struct_size,
|
||||
ndr.ndr_push, ndr.ndr_pull);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
|
@ -101,6 +101,9 @@ struct dcerpc_pipe {
|
||||
|
||||
#define DCERPC_AUTH_OPTIONS (DCERPC_SEAL|DCERPC_SIGN|DCERPC_SCHANNEL_ANY)
|
||||
|
||||
/* check incoming pad bytes */
|
||||
#define DCERPC_DEBUG_PAD_CHECK (1<<12)
|
||||
|
||||
/*
|
||||
this is used to find pointers to calls
|
||||
*/
|
||||
|
@ -277,6 +277,7 @@ static const struct {
|
||||
{"seal", DCERPC_SEAL},
|
||||
{"validate", DCERPC_DEBUG_VALIDATE_BOTH},
|
||||
{"print", DCERPC_DEBUG_PRINT_BOTH},
|
||||
{"padcheck", DCERPC_DEBUG_PAD_CHECK},
|
||||
{"bigendian", DCERPC_PUSH_BIGENDIAN}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user