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:
parent
7b265830ad
commit
e737988641
@ -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() */
|
||||
|
@ -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",
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
@ -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__ */
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user