summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2021-04-02 13:41:21 +0200
committerJule Anger <janger@samba.org>2021-11-08 10:52:13 +0100
commit4a893891951d9b3e087e6577327c3aa0a019e99e (patch)
tree0130746b760d5bf3ceae0493c6f2566b54466237
parent83a9fb52f3e32422b4b2ab6327ad06c35c71ae1a (diff)
downloadsamba-4a893891951d9b3e087e6577327c3aa0a019e99e.tar.gz
CVE-2021-23192 librpc: Remove the gensec dependency from library dcerpc-binding
This means yet another library, but having to depend on gensec just for dcerpc_parse_binding() and basic packet parsing seems like a bit overkill to me. Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org> Autobuild-User(master): Jeremy Allison <jra@samba.org> Autobuild-Date(master): Tue Apr 6 23:33:14 UTC 2021 on sn-devel-184 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14875 (cherry picked from commit 4d3b6506d30e4bf302f832493dad00a83b73d370)
-rw-r--r--libcli/auth/wscript_build10
-rw-r--r--librpc/rpc/dcerpc_pkt_auth.c497
-rw-r--r--librpc/rpc/dcerpc_pkt_auth.h58
-rw-r--r--librpc/rpc/dcerpc_util.c464
-rw-r--r--librpc/rpc/dcerpc_util.h18
-rw-r--r--librpc/rpc/dcesrv_auth.c1
-rw-r--r--librpc/wscript_build25
-rw-r--r--source3/wscript_build8
-rw-r--r--source4/librpc/rpc/dcerpc.c1
-rw-r--r--source4/librpc/wscript_build21
10 files changed, 613 insertions, 490 deletions
diff --git a/libcli/auth/wscript_build b/libcli/auth/wscript_build
index 2a6a7468e45..24ab68fac1e 100644
--- a/libcli/auth/wscript_build
+++ b/libcli/auth/wscript_build
@@ -30,7 +30,15 @@ bld.SAMBA_SUBSYSTEM('COMMON_SCHANNEL',
bld.SAMBA_SUBSYSTEM('NETLOGON_CREDS_CLI',
source='netlogon_creds_cli.c',
- deps='dbwrap util_tdb tevent-util samba-hostconfig RPC_NDR_NETLOGON NDR_NETLOGON'
+ deps='''
+ dbwrap
+ util_tdb
+ tevent-util
+ samba-hostconfig
+ gensec
+ RPC_NDR_NETLOGON
+ NDR_NETLOGON
+ '''
)
bld.SAMBA_SUBSYSTEM('PAM_ERRORS',
diff --git a/librpc/rpc/dcerpc_pkt_auth.c b/librpc/rpc/dcerpc_pkt_auth.c
new file mode 100644
index 00000000000..322d7497893
--- /dev/null
+++ b/librpc/rpc/dcerpc_pkt_auth.c
@@ -0,0 +1,497 @@
+/*
+ Unix SMB/CIFS implementation.
+ raw dcerpc operations
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Jelmer Vernooij 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/util/talloc_stack.h"
+#include "lib/util/debug.h"
+#include "lib/util/byteorder.h"
+#include "lib/util/samba_util.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_util.h"
+#include "librpc/rpc/dcerpc_pkt_auth.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#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>
+
+NTSTATUS dcerpc_ncacn_pull_pkt_auth(const struct dcerpc_auth *auth_state,
+ struct gensec_security *gensec,
+ TALLOC_CTX *mem_ctx,
+ enum dcerpc_pkt_type ptype,
+ uint8_t required_flags,
+ uint8_t optional_flags,
+ uint8_t payload_offset,
+ DATA_BLOB *payload_and_verifier,
+ DATA_BLOB *raw_packet,
+ const struct ncacn_packet *pkt)
+{
+ NTSTATUS status;
+ struct dcerpc_auth auth;
+ uint32_t auth_length;
+
+ if (auth_state == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = dcerpc_verify_ncacn_packet_header(pkt, ptype,
+ payload_and_verifier->length,
+ required_flags, optional_flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (auth_state->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PACKET:
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ if (pkt->auth_length != 0) {
+ break;
+ }
+ return NT_STATUS_OK;
+ case DCERPC_AUTH_LEVEL_NONE:
+ if (pkt->auth_length != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+
+ default:
+ return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
+ }
+
+ if (pkt->auth_length == 0) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ if (gensec == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = dcerpc_pull_auth_trailer(pkt, mem_ctx,
+ payload_and_verifier,
+ &auth, &auth_length, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (payload_and_verifier->length < auth_length) {
+ /*
+ * should be checked in dcerpc_pull_auth_trailer()
+ */
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ payload_and_verifier->length -= auth_length;
+
+ if (payload_and_verifier->length < auth.auth_pad_length) {
+ /*
+ * should be checked in dcerpc_pull_auth_trailer()
+ */
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (auth.auth_type != auth_state->auth_type) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (auth.auth_level != auth_state->auth_level) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (auth.auth_context_id != auth_state->auth_context_id) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* check signature or unseal the packet */
+ switch (auth_state->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = gensec_unseal_packet(gensec,
+ raw_packet->data + payload_offset,
+ payload_and_verifier->length,
+ raw_packet->data,
+ raw_packet->length -
+ auth.credentials.length,
+ &auth.credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_RPC_SEC_PKG_ERROR;
+ }
+ memcpy(payload_and_verifier->data,
+ raw_packet->data + payload_offset,
+ payload_and_verifier->length);
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PACKET:
+ status = gensec_check_packet(gensec,
+ payload_and_verifier->data,
+ payload_and_verifier->length,
+ raw_packet->data,
+ raw_packet->length -
+ auth.credentials.length,
+ &auth.credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_RPC_SEC_PKG_ERROR;
+ }
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ /* for now we ignore possible signatures here */
+ break;
+
+ default:
+ return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
+ }
+
+ /*
+ * remove the indicated amount of padding
+ *
+ * A possible overflow is checked above.
+ */
+ payload_and_verifier->length -= auth.auth_pad_length;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS dcerpc_ncacn_push_pkt_auth(const struct dcerpc_auth *auth_state,
+ struct gensec_security *gensec,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *raw_packet,
+ size_t sig_size,
+ uint8_t payload_offset,
+ const DATA_BLOB *payload,
+ const struct ncacn_packet *pkt)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ struct ndr_push *ndr = NULL;
+ uint32_t payload_length;
+ uint32_t whole_length;
+ DATA_BLOB blob = data_blob_null;
+ DATA_BLOB sig = data_blob_null;
+ struct dcerpc_auth _out_auth_info;
+ struct dcerpc_auth *out_auth_info = NULL;
+
+ *raw_packet = data_blob_null;
+
+ if (auth_state == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ switch (auth_state->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PACKET:
+ if (sig_size == 0) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (gensec == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ _out_auth_info = (struct dcerpc_auth) {
+ .auth_type = auth_state->auth_type,
+ .auth_level = auth_state->auth_level,
+ .auth_context_id = auth_state->auth_context_id,
+ };
+ out_auth_info = &_out_auth_info;
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ /*
+ * TODO: let the gensec mech decide if it wants to generate a
+ * signature that might be needed for schannel...
+ */
+ if (sig_size != 0) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (gensec == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ break;
+
+ case DCERPC_AUTH_LEVEL_NONE:
+ if (sig_size != 0) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ break;
+
+ default:
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ndr = ndr_push_init_ctx(frame);
+ if (ndr == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(frame);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (out_auth_info != NULL) {
+ /*
+ * pad to 16 byte multiple in the payload portion of the
+ * packet. This matches what w2k3 does. Note that we can't use
+ * ndr_push_align() as that is relative to the start of the
+ * whole packet, whereas w2k8 wants it relative to the start
+ * of the stub.
+ */
+ out_auth_info->auth_pad_length =
+ DCERPC_AUTH_PAD_LENGTH(payload->length);
+ ndr_err = ndr_push_zero(ndr, out_auth_info->auth_pad_length);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(frame);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ payload_length = payload->length +
+ out_auth_info->auth_pad_length;
+
+ ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
+ out_auth_info);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(frame);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ whole_length = ndr->offset;
+
+ ndr_err = ndr_push_zero(ndr, sig_size);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(frame);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ } else {
+ payload_length = payload->length;
+ whole_length = ndr->offset;
+ }
+
+ /* extract the whole packet as a blob */
+ blob = ndr_push_blob(ndr);
+
+ /*
+ * Setup the frag and auth length in the packet buffer.
+ * This is needed if the GENSEC mech does AEAD signing
+ * of the packet headers. The signature itself will be
+ * appended later.
+ */
+ dcerpc_set_frag_length(&blob, blob.length);
+ dcerpc_set_auth_length(&blob, sig_size);
+
+ /* sign or seal the packet */
+ switch (auth_state->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = gensec_seal_packet(gensec,
+ frame,
+ blob.data + payload_offset,
+ payload_length,
+ blob.data,
+ whole_length,
+ &sig);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ case DCERPC_AUTH_LEVEL_PACKET:
+ status = gensec_sign_packet(gensec,
+ frame,
+ blob.data + payload_offset,
+ payload_length,
+ blob.data,
+ whole_length,
+ &sig);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ case DCERPC_AUTH_LEVEL_NONE:
+ break;
+
+ default:
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (sig.length != sig_size) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_RPC_SEC_PKG_ERROR;
+ }
+
+ if (sig_size != 0) {
+ memcpy(blob.data + whole_length, sig.data, sig_size);
+ }
+
+ *raw_packet = blob;
+ talloc_steal(mem_ctx, raw_packet->data);
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+#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() */
diff --git a/librpc/rpc/dcerpc_pkt_auth.h b/librpc/rpc/dcerpc_pkt_auth.h
new file mode 100644
index 00000000000..c0d23b91c05
--- /dev/null
+++ b/librpc/rpc/dcerpc_pkt_auth.h
@@ -0,0 +1,58 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2010-2011
+ Copyright (C) Andrew Tridgell 2010-2011
+ Copyright (C) Simo Sorce 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBRPC_RPC_DCERPC_PKT_AUTH_H__
+#define __LIBRPC_RPC_DCERPC_PKT_AUTH_H__
+
+#include "replace.h"
+#include <talloc.h>
+#include "lib/util/data_blob.h"
+#include "libcli/util/ntstatus.h"
+#include "librpc/rpc/rpc_common.h"
+#include "librpc/gen_ndr/dcerpc.h"
+
+NTSTATUS dcerpc_ncacn_pull_pkt_auth(const struct dcerpc_auth *auth_state,
+ struct gensec_security *gensec,
+ TALLOC_CTX *mem_ctx,
+ enum dcerpc_pkt_type ptype,
+ uint8_t required_flags,
+ uint8_t optional_flags,
+ uint8_t payload_offset,
+ DATA_BLOB *payload_and_verifier,
+ DATA_BLOB *raw_packet,
+ const struct ncacn_packet *pkt);
+NTSTATUS dcerpc_ncacn_push_pkt_auth(const struct dcerpc_auth *auth_state,
+ struct gensec_security *gensec,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *raw_packet,
+ size_t sig_size,
+ uint8_t payload_offset,
+ const DATA_BLOB *payload,
+ const struct ncacn_packet *pkt);
+struct tevent_req *dcerpc_read_ncacn_packet_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *stream);
+NTSTATUS dcerpc_read_ncacn_packet_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct ncacn_packet **pkt,
+ DATA_BLOB *buffer);
+
+#endif
diff --git a/librpc/rpc/dcerpc_util.c b/librpc/rpc/dcerpc_util.c
index 6a160e12023..f1b80efde69 100644
--- a/librpc/rpc/dcerpc_util.c
+++ b/librpc/rpc/dcerpc_util.c
@@ -29,10 +29,6 @@
#include "librpc/gen_ndr/ndr_dcerpc.h"
#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 */
@@ -484,340 +480,6 @@ NTSTATUS dcerpc_verify_ncacn_packet_header(const struct ncacn_packet *pkt,
return NT_STATUS_OK;
}
-NTSTATUS dcerpc_ncacn_pull_pkt_auth(const struct dcerpc_auth *auth_state,
- struct gensec_security *gensec,
- TALLOC_CTX *mem_ctx,
- enum dcerpc_pkt_type ptype,
- uint8_t required_flags,
- uint8_t optional_flags,
- uint8_t payload_offset,
- DATA_BLOB *payload_and_verifier,
- DATA_BLOB *raw_packet,
- const struct ncacn_packet *pkt)
-{
- NTSTATUS status;
- struct dcerpc_auth auth;
- uint32_t auth_length;
-
- if (auth_state == NULL) {
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- status = dcerpc_verify_ncacn_packet_header(pkt, ptype,
- payload_and_verifier->length,
- required_flags, optional_flags);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- switch (auth_state->auth_level) {
- case DCERPC_AUTH_LEVEL_PRIVACY:
- case DCERPC_AUTH_LEVEL_INTEGRITY:
- case DCERPC_AUTH_LEVEL_PACKET:
- break;
-
- case DCERPC_AUTH_LEVEL_CONNECT:
- if (pkt->auth_length != 0) {
- break;
- }
- return NT_STATUS_OK;
- case DCERPC_AUTH_LEVEL_NONE:
- if (pkt->auth_length != 0) {
- return NT_STATUS_ACCESS_DENIED;
- }
- return NT_STATUS_OK;
-
- default:
- return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
- }
-
- if (pkt->auth_length == 0) {
- return NT_STATUS_RPC_PROTOCOL_ERROR;
- }
-
- if (gensec == NULL) {
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- status = dcerpc_pull_auth_trailer(pkt, mem_ctx,
- payload_and_verifier,
- &auth, &auth_length, false);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- if (payload_and_verifier->length < auth_length) {
- /*
- * should be checked in dcerpc_pull_auth_trailer()
- */
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- payload_and_verifier->length -= auth_length;
-
- if (payload_and_verifier->length < auth.auth_pad_length) {
- /*
- * should be checked in dcerpc_pull_auth_trailer()
- */
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- if (auth.auth_type != auth_state->auth_type) {
- return NT_STATUS_ACCESS_DENIED;
- }
-
- if (auth.auth_level != auth_state->auth_level) {
- return NT_STATUS_ACCESS_DENIED;
- }
-
- if (auth.auth_context_id != auth_state->auth_context_id) {
- return NT_STATUS_ACCESS_DENIED;
- }
-
- /* check signature or unseal the packet */
- switch (auth_state->auth_level) {
- case DCERPC_AUTH_LEVEL_PRIVACY:
- status = gensec_unseal_packet(gensec,
- raw_packet->data + payload_offset,
- payload_and_verifier->length,
- raw_packet->data,
- raw_packet->length -
- auth.credentials.length,
- &auth.credentials);
- if (!NT_STATUS_IS_OK(status)) {
- return NT_STATUS_RPC_SEC_PKG_ERROR;
- }
- memcpy(payload_and_verifier->data,
- raw_packet->data + payload_offset,
- payload_and_verifier->length);
- break;
-
- case DCERPC_AUTH_LEVEL_INTEGRITY:
- case DCERPC_AUTH_LEVEL_PACKET:
- status = gensec_check_packet(gensec,
- payload_and_verifier->data,
- payload_and_verifier->length,
- raw_packet->data,
- raw_packet->length -
- auth.credentials.length,
- &auth.credentials);
- if (!NT_STATUS_IS_OK(status)) {
- return NT_STATUS_RPC_SEC_PKG_ERROR;
- }
- break;
-
- case DCERPC_AUTH_LEVEL_CONNECT:
- /* for now we ignore possible signatures here */
- break;
-
- default:
- return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
- }
-
- /*
- * remove the indicated amount of padding
- *
- * A possible overflow is checked above.
- */
- payload_and_verifier->length -= auth.auth_pad_length;
-
- return NT_STATUS_OK;
-}
-
-NTSTATUS dcerpc_ncacn_push_pkt_auth(const struct dcerpc_auth *auth_state,
- struct gensec_security *gensec,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *raw_packet,
- size_t sig_size,
- uint8_t payload_offset,
- const DATA_BLOB *payload,
- const struct ncacn_packet *pkt)
-{
- TALLOC_CTX *frame = talloc_stackframe();
- NTSTATUS status;
- enum ndr_err_code ndr_err;
- struct ndr_push *ndr = NULL;
- uint32_t payload_length;
- uint32_t whole_length;
- DATA_BLOB blob = data_blob_null;
- DATA_BLOB sig = data_blob_null;
- struct dcerpc_auth _out_auth_info;
- struct dcerpc_auth *out_auth_info = NULL;
-
- *raw_packet = data_blob_null;
-
- if (auth_state == NULL) {
- TALLOC_FREE(frame);
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- switch (auth_state->auth_level) {
- case DCERPC_AUTH_LEVEL_PRIVACY:
- case DCERPC_AUTH_LEVEL_INTEGRITY:
- case DCERPC_AUTH_LEVEL_PACKET:
- if (sig_size == 0) {
- TALLOC_FREE(frame);
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- if (gensec == NULL) {
- TALLOC_FREE(frame);
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- _out_auth_info = (struct dcerpc_auth) {
- .auth_type = auth_state->auth_type,
- .auth_level = auth_state->auth_level,
- .auth_context_id = auth_state->auth_context_id,
- };
- out_auth_info = &_out_auth_info;
- break;
-
- case DCERPC_AUTH_LEVEL_CONNECT:
- /*
- * TODO: let the gensec mech decide if it wants to generate a
- * signature that might be needed for schannel...
- */
- if (sig_size != 0) {
- TALLOC_FREE(frame);
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- if (gensec == NULL) {
- TALLOC_FREE(frame);
- return NT_STATUS_INTERNAL_ERROR;
- }
- break;
-
- case DCERPC_AUTH_LEVEL_NONE:
- if (sig_size != 0) {
- TALLOC_FREE(frame);
- return NT_STATUS_INTERNAL_ERROR;
- }
- break;
-
- default:
- TALLOC_FREE(frame);
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- ndr = ndr_push_init_ctx(frame);
- if (ndr == NULL) {
- TALLOC_FREE(frame);
- return NT_STATUS_NO_MEMORY;
- }
-
- ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
- if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
- TALLOC_FREE(frame);
- return ndr_map_error2ntstatus(ndr_err);
- }
-
- if (out_auth_info != NULL) {
- /*
- * pad to 16 byte multiple in the payload portion of the
- * packet. This matches what w2k3 does. Note that we can't use
- * ndr_push_align() as that is relative to the start of the
- * whole packet, whereas w2k8 wants it relative to the start
- * of the stub.
- */
- out_auth_info->auth_pad_length =
- DCERPC_AUTH_PAD_LENGTH(payload->length);
- ndr_err = ndr_push_zero(ndr, out_auth_info->auth_pad_length);
- if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
- TALLOC_FREE(frame);
- return ndr_map_error2ntstatus(ndr_err);
- }
-
- payload_length = payload->length +
- out_auth_info->auth_pad_length;
-
- ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
- out_auth_info);
- if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
- TALLOC_FREE(frame);
- return ndr_map_error2ntstatus(ndr_err);
- }
-
- whole_length = ndr->offset;
-
- ndr_err = ndr_push_zero(ndr, sig_size);
- if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
- TALLOC_FREE(frame);
- return ndr_map_error2ntstatus(ndr_err);
- }
- } else {
- payload_length = payload->length;
- whole_length = ndr->offset;
- }
-
- /* extract the whole packet as a blob */
- blob = ndr_push_blob(ndr);
-
- /*
- * Setup the frag and auth length in the packet buffer.
- * This is needed if the GENSEC mech does AEAD signing
- * of the packet headers. The signature itself will be
- * appended later.
- */
- dcerpc_set_frag_length(&blob, blob.length);
- dcerpc_set_auth_length(&blob, sig_size);
-
- /* sign or seal the packet */
- switch (auth_state->auth_level) {
- case DCERPC_AUTH_LEVEL_PRIVACY:
- status = gensec_seal_packet(gensec,
- frame,
- blob.data + payload_offset,
- payload_length,
- blob.data,
- whole_length,
- &sig);
- if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(frame);
- return status;
- }
- break;
-
- case DCERPC_AUTH_LEVEL_INTEGRITY:
- case DCERPC_AUTH_LEVEL_PACKET:
- status = gensec_sign_packet(gensec,
- frame,
- blob.data + payload_offset,
- payload_length,
- blob.data,
- whole_length,
- &sig);
- if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(frame);
- return status;
- }
- break;
-
- case DCERPC_AUTH_LEVEL_CONNECT:
- case DCERPC_AUTH_LEVEL_NONE:
- break;
-
- default:
- TALLOC_FREE(frame);
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- if (sig.length != sig_size) {
- TALLOC_FREE(frame);
- return NT_STATUS_RPC_SEC_PKG_ERROR;
- }
-
- if (sig_size != 0) {
- memcpy(blob.data + whole_length, sig.data, sig_size);
- }
-
- *raw_packet = blob;
- talloc_steal(mem_ctx, raw_packet->data);
- TALLOC_FREE(frame);
- return NT_STATUS_OK;
-}
-
struct dcerpc_read_ncacn_packet_state {
#if 0
struct {
@@ -1472,129 +1134,3 @@ 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() */
diff --git a/librpc/rpc/dcerpc_util.h b/librpc/rpc/dcerpc_util.h
index 0ecaf428c3c..a9bc7bd3832 100644
--- a/librpc/rpc/dcerpc_util.h
+++ b/librpc/rpc/dcerpc_util.h
@@ -74,24 +74,6 @@ NTSTATUS dcerpc_verify_ncacn_packet_header(const struct ncacn_packet *pkt,
size_t max_auth_info,
uint8_t required_flags,
uint8_t optional_flags);
-NTSTATUS dcerpc_ncacn_pull_pkt_auth(const struct dcerpc_auth *auth_state,
- struct gensec_security *gensec,
- TALLOC_CTX *mem_ctx,
- enum dcerpc_pkt_type ptype,
- uint8_t required_flags,
- uint8_t optional_flags,
- uint8_t payload_offset,
- DATA_BLOB *payload_and_verifier,
- DATA_BLOB *raw_packet,
- const struct ncacn_packet *pkt);
-NTSTATUS dcerpc_ncacn_push_pkt_auth(const struct dcerpc_auth *auth_state,
- struct gensec_security *gensec,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *raw_packet,
- size_t sig_size,
- uint8_t payload_offset,
- const DATA_BLOB *payload,
- const struct ncacn_packet *pkt);
struct tevent_req *dcerpc_read_ncacn_packet_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct tstream_context *stream);
diff --git a/librpc/rpc/dcesrv_auth.c b/librpc/rpc/dcesrv_auth.c
index f7919ebf575..8dda86d88e2 100644
--- a/librpc/rpc/dcesrv_auth.c
+++ b/librpc/rpc/dcesrv_auth.c
@@ -24,6 +24,7 @@
#include "librpc/rpc/dcesrv_core.h"
#include "librpc/rpc/dcesrv_core_proto.h"
#include "librpc/rpc/dcerpc_util.h"
+#include "librpc/rpc/dcerpc_pkt_auth.h"
#include "librpc/gen_ndr/ndr_dcerpc.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
diff --git a/librpc/wscript_build b/librpc/wscript_build
index 8f31d59d3b5..a1c3c994876 100644
--- a/librpc/wscript_build
+++ b/librpc/wscript_build
@@ -655,12 +655,24 @@ bld.SAMBA_LIBRARY('ndr',
)
bld.SAMBA_LIBRARY('dcerpc-binding',
- source='rpc/dcerpc_error.c rpc/binding.c rpc/dcerpc_util.c rpc/binding_handle.c',
- deps='ndr tevent NDR_DCERPC LIBTSOCKET tevent-util gensec',
+ source='''
+ rpc/dcerpc_error.c
+ rpc/binding.c
+ rpc/dcerpc_util.c
+ rpc/binding_handle.c
+ ''',
+ deps='ndr tevent NDR_DCERPC LIBTSOCKET tevent-util',
pc_files=[],
public_headers='rpc/rpc_common.h',
vnum='0.0.1')
+bld.SAMBA_LIBRARY('dcerpc-pkt-auth',
+ private_library=True,
+ source='''
+ rpc/dcerpc_pkt_auth.c
+ ''',
+ deps='dcerpc-binding gensec')
+
bld.SAMBA_LIBRARY('dcerpc-server-core',
source='''
rpc/dcesrv_core.c
@@ -669,7 +681,14 @@ bld.SAMBA_LIBRARY('dcerpc-server-core',
rpc/dcesrv_reply.c
rpc/dcesrv_handles.c
''',
- deps='ndr dcerpc-binding samba-util-core gnutls GNUTLS_HELPERS',
+ deps='''
+ ndr
+ dcerpc-binding
+ samba-util-core
+ gnutls
+ GNUTLS_HELPERS
+ dcerpc-pkt-auth
+ ''',
pc_files=[],
public_headers='rpc/dcesrv_core.h',
autoproto='rpc/dcesrv_core_proto.h',
diff --git a/source3/wscript_build b/source3/wscript_build
index d86a9fcadbf..46c914c7b22 100644
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1032,9 +1032,11 @@ bld.SAMBA3_LIBRARY('cli_spoolss',
rpc_client/init_spoolss.c
''',
deps='''
- RPC_NDR_SPOOLSS
- smbconf
- secrets3''',
+ RPC_NDR_SPOOLSS
+ smbconf
+ secrets3
+ gensec
+ ''',
private_library=True)
bld.SAMBA3_SUBSYSTEM('LIBCLI_WINREG',
diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c
index f97263dcc5b..4847e8a0200 100644
--- a/source4/librpc/rpc/dcerpc.c
+++ b/source4/librpc/rpc/dcerpc.c
@@ -27,6 +27,7 @@
#include "librpc/rpc/dcerpc.h"
#include "librpc/rpc/dcerpc_proto.h"
#include "librpc/rpc/dcerpc_util.h"
+#include "librpc/rpc/dcerpc_pkt_auth.h"
#include "librpc/gen_ndr/ndr_misc.h"
#include "librpc/gen_ndr/ndr_dcerpc.h"
#include "auth/gensec/gensec.h"
diff --git a/source4/librpc/wscript_build b/source4/librpc/wscript_build
index ea9c4853d7a..511008d919d 100644
--- a/source4/librpc/wscript_build
+++ b/source4/librpc/wscript_build
@@ -157,7 +157,26 @@ bld.SAMBA_LIBRARY('dcerpc',
rpc/dcerpc_roh_channel_in.c rpc/dcerpc_roh_channel_out.c rpc/dcerpc_roh.c
rpc/dcerpc_connect.c rpc/dcerpc_secondary.c''',
pc_files='dcerpc.pc',
- deps='samba_socket LIBCLI_RESOLVE LIBCLI_SMB LIBCLI_SMB2 ndr NDR_DCERPC RPC_NDR_EPMAPPER NDR_SCHANNEL RPC_NDR_NETLOGON RPC_NDR_MGMT gensec LIBCLI_AUTH smbclient-raw LP_RESOLVE tevent-util dcerpc-binding param_options http',
+ deps='''
+ samba_socket
+ LIBCLI_RESOLVE
+ LIBCLI_SMB
+ LIBCLI_SMB2
+ ndr
+ NDR_DCERPC
+ RPC_NDR_EPMAPPER
+ NDR_SCHANNEL
+ RPC_NDR_NETLOGON
+ RPC_NDR_MGMT
+ gensec
+ LIBCLI_AUTH
+ smbclient-raw
+ LP_RESOLVE
+ tevent-util
+ dcerpc-binding
+ dcerpc-pkt-auth
+ param_options
+ http''',
autoproto='rpc/dcerpc_proto.h',
public_deps='samba-credentials tevent talloc',
public_headers='''rpc/dcerpc.h''',