#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 */
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() */
#include "librpc/gen_ndr/ndr_dcerpc.h"
#include "lib/util/tevent_ntstatus.h"
+
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_RPC_SRV
#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();
} 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",
_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_ */
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
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__ */
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',
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);
print CONF "
[global]
+ dcesrv:fuzz directory = $cachedir/fuzz
netbios name = $server
interfaces = $interfaces
bind interfaces only = yes
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"));
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);
&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;
}