1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-27 22:50:26 +03:00

dcerpc: developer option to save ndr_fuzz_X seeds

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Pair-programmed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
Douglas Bagnall 2019-11-06 17:27:08 +13:00 committed by Andrew Bartlett
parent 7b265830ad
commit e737988641
8 changed files with 224 additions and 1 deletions

View File

@ -29,6 +29,9 @@
#include "rpc_common.h"
#include "lib/util/bitmap.h"
#include "auth/gensec/gensec.h"
#include "lib/util/mkdir_p.h"
#include "lib/crypto/gnutls_helpers.h"
#include <gnutls/crypto.h>
/* we need to be able to get/set the fragment length without doing a full
decode */
@ -1447,3 +1450,129 @@ void dcerpc_log_packet(const char *packet_log_dir,
free(name);
}
}
#ifdef DEVELOPER
/*
* Save valid, well-formed DCE/RPC stubs to use as a seed for
* ndr_fuzz_X
*/
void dcerpc_save_ndr_fuzz_seed(TALLOC_CTX *mem_ctx,
DATA_BLOB raw_blob,
const char *dump_dir,
const char *iface_name,
int flags,
int opnum,
bool ndr64)
{
char *fname = NULL;
const char *sub_dir = NULL;
TALLOC_CTX *temp_ctx = talloc_new(mem_ctx);
DATA_BLOB blob;
int ret, rc;
uint8_t digest[20];
DATA_BLOB digest_blob;
char *digest_hex;
uint16_t fuzz_flags = 0;
/*
* We want to save the 'stub' in a per-pipe subdirectory, with
* the ndr_fuzz_X header 4 byte header. For the sake of
* convenience (this is a developer only function), we mkdir
* -p the sub-directories when they are needed.
*/
if (dump_dir == NULL) {
return;
}
temp_ctx = talloc_stackframe();
sub_dir = talloc_asprintf(temp_ctx, "%s/%s",
dump_dir,
iface_name);
if (sub_dir == NULL) {
talloc_free(temp_ctx);
return;
}
ret = mkdir_p(sub_dir, 0755);
if (ret && errno != EEXIST) {
DBG_ERR("could not create %s\n", sub_dir);
talloc_free(temp_ctx);
return;
}
blob.length = raw_blob.length + 4;
blob.data = talloc_array(sub_dir,
uint8_t,
blob.length);
if (blob.data == NULL) {
DBG_ERR("could not allocate for fuzz seeds! (%s)\n",
iface_name);
talloc_free(temp_ctx);
return;
}
if (ndr64) {
fuzz_flags = 4;
}
if (flags & NDR_IN) {
fuzz_flags |= 1;
} else if (flags & NDR_OUT) {
fuzz_flags |= 2;
}
SSVAL(blob.data, 0, fuzz_flags);
SSVAL(blob.data, 2, opnum);
memcpy(&blob.data[4],
raw_blob.data,
raw_blob.length);
/*
* This matches how oss-fuzz names the corpus input files, due
* to a preference from libFuzzer
*/
rc = gnutls_hash_fast(GNUTLS_DIG_SHA1,
blob.data,
blob.length,
digest);
if (rc < 0) {
/*
* This prints a better error message, eg if SHA1 is
* disabled
*/
NTSTATUS status = gnutls_error_to_ntstatus(rc,
NT_STATUS_HASH_NOT_SUPPORTED);
DBG_ERR("Failed to generate SHA1 to save fuzz seed: %s",
nt_errstr(status));
talloc_free(temp_ctx);
return;
}
digest_blob.data = digest;
digest_blob.length = sizeof(digest);
digest_hex = data_blob_hex_string_lower(temp_ctx, &digest_blob);
fname = talloc_asprintf(temp_ctx, "%s/%s",
sub_dir,
digest_hex);
if (fname == NULL) {
talloc_free(temp_ctx);
return;
}
/*
* If this fails, it is most likely because that file already
* exists. This is fine, it means we already have this
* sample
*/
file_save(fname,
blob.data,
blob.length);
talloc_free(temp_ctx);
}
#endif /*if DEVELOPER, enveloping _dcesrv_save_ndr_fuzz_seed() */

View File

@ -33,6 +33,7 @@
#include "librpc/gen_ndr/ndr_dcerpc.h"
#include "lib/util/tevent_ntstatus.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_RPC_SRV
@ -1705,6 +1706,32 @@ static void dcesrv_save_call(struct dcesrv_call_state *call, const char *why)
#endif
}
#ifdef DEVELOPER
/*
Save the call for use as a seed for fuzzing.
This is only enabled in a developer build, and only has effect if the
"dcesrv fuzz directory" param is set.
*/
void _dcesrv_save_ndr_fuzz_seed(DATA_BLOB call_blob,
struct dcesrv_call_state *call,
int flags)
{
const char *dump_dir = lpcfg_parm_string(call->conn->dce_ctx->lp_ctx,
NULL,
"dcesrv", "fuzz directory");
dcerpc_save_ndr_fuzz_seed(call,
call_blob,
dump_dir,
call->context->iface->name,
flags,
call->pkt.u.request.opnum,
call->ndr_pull->flags & LIBNDR_FLAG_NDR64);
}
#endif /*if DEVELOPER, enveloping _dcesrv_save_ndr_fuzz_seed() */
static NTSTATUS dcesrv_check_verification_trailer(struct dcesrv_call_state *call)
{
TALLOC_CTX *frame = talloc_stackframe();
@ -1848,9 +1875,14 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
} else {
dcesrv_save_call(call, "pullfail");
}
return dcesrv_fault_with_flags(call, call->fault_code, extra_flags);
}
dcesrv_save_ndr_fuzz_seed(call->pkt.u.request.stub_and_verifier,
call,
NDR_IN);
if (pull->offset != pull->data_size) {
dcesrv_save_call(call, "extrabytes");
DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",

View File

@ -605,4 +605,18 @@ _PUBLIC_ void dcesrv_sock_report_output_data(struct dcesrv_connection *dce_conn)
_PUBLIC_ NTSTATUS dcesrv_connection_loop_start(struct dcesrv_connection *conn);
void _dcesrv_save_ndr_fuzz_seed(DATA_BLOB call_blob,
struct dcesrv_call_state *call,
int flags);
#if DEVELOPER
#define dcesrv_save_ndr_fuzz_seed(stub, call, flags) \
_dcesrv_save_ndr_fuzz_seed(stub, call, flags)
#else
#define dcesrv_save_ndr_fuzz_seed(stub, call, flags) \
/* */
#endif
#endif /* _LIBRPC_RPC_DCESRV_CORE_H_ */

View File

@ -174,6 +174,10 @@ _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
stub = ndr_push_blob(push);
dcesrv_save_ndr_fuzz_seed(stub,
call,
NDR_OUT);
total_length = stub.length;
/* we can write a full max_recv_frag size, minus the dcerpc

View File

@ -458,4 +458,25 @@ void dcerpc_log_packet(const char *packet_log_dir,
const DATA_BLOB *pkt,
const char *why);
#ifdef DEVELOPER
void dcerpc_save_ndr_fuzz_seed(TALLOC_CTX *mem_ctx,
DATA_BLOB raw_blob,
const char *dump_dir,
const char *iface_name,
int flags,
int opnum,
bool ndr64);
#else
static inline void dcerpc_save_ndr_fuzz_seed(TALLOC_CTX *mem_ctx,
DATA_BLOB raw_blob,
const char *dump_dir,
const char *iface_name,
int flags,
int opnum,
bool ndr64)
{
return;
}
#endif
#endif /* __DEFAULT_LIBRPC_RPCCOMMON_H__ */

View File

@ -639,7 +639,7 @@ bld.SAMBA_LIBRARY('dcerpc-server-core',
rpc/dcesrv_mgmt.c
rpc/dcesrv_reply.c
''',
deps='ndr dcerpc-binding',
deps='ndr dcerpc-binding samba-util-core gnutls GNUTLS_HELPERS',
pc_files=[],
public_headers='rpc/dcesrv_core.h',
autoproto='rpc/dcesrv_core_proto.h',

View File

@ -1436,6 +1436,9 @@ sub provision($$$$$$$$$)
my $privatedir="$prefix_abs/private";
push(@dirs,$privatedir);
my $cachedir = "$prefix_abs/cachedir";
push(@dirs, $cachedir);
my $binddnsdir = "$prefix_abs/bind-dns";
push(@dirs, $binddnsdir);
@ -1702,6 +1705,7 @@ sub provision($$$$$$$$$)
print CONF "
[global]
dcesrv:fuzz directory = $cachedir/fuzz
netbios name = $server
interfaces = $interfaces
bind interfaces only = yes

View File

@ -1401,6 +1401,7 @@ static bool api_pipe_request(struct pipes_struct *p,
bool ret = False;
struct pipe_rpc_fns *pipe_fns;
const char *interface_name = NULL;
const char *dump_dir = NULL;
if (!p->pipe_bound) {
DEBUG(1, ("Pipe not bound!\n"));
@ -1465,6 +1466,16 @@ static bool api_pipe_request(struct pipes_struct *p,
break;
}
dump_dir = lp_parm_const_string(0, "dcesrv", "fuzz directory", NULL);
dcerpc_save_ndr_fuzz_seed(p,
p->in_data.data,
dump_dir,
interface_name,
NDR_IN,
pkt->u.request.opnum,
false);
if (!srv_pipe_check_verification_trailer(p, pkt, pipe_fns)) {
DEBUG(1, ("srv_pipe_check_verification_trailer: failed\n"));
set_incoming_fault(p);
@ -1487,6 +1498,14 @@ static bool api_pipe_request(struct pipes_struct *p,
&pipe_fns->syntax);
unbecome_authenticated_pipe_user();
dcerpc_save_ndr_fuzz_seed(p,
p->out_data.rdata,
dump_dir,
interface_name,
NDR_OUT,
pkt->u.request.opnum,
false);
TALLOC_FREE(frame);
return ret;
}